mysqltest.c 121 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000 MySQL AB
2

unknown's avatar
unknown committed
3 4 5 6
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
7

unknown's avatar
unknown committed
8 9 10 11
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
12

unknown's avatar
unknown committed
13 14 15 16 17
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/* mysqltest test tool
18 19
 * See the manual for more information
 * TODO: document better how mysqltest works
unknown's avatar
unknown committed
20 21 22 23
 *
 * Written by:
 *   Sasha Pachev <sasha@mysql.com>
 *   Matt Wagner  <matt@mysql.com>
24
 *   Monty
25
 *   Jani
unknown's avatar
unknown committed
26 27
 **/

28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
/**********************************************************************
  TODO:

- Do comparison line by line, instead of doing a full comparison of
  the text file.  This will save space as we don't need to keep many
  results in memory.  It will also make it possible to do simple
  'comparison' fixes like accepting the result even if a float differed
  in the last decimals.

- Don't buffer lines from the test that you don't expect to need
  again.

- Change 'read_line' to be faster by using the readline.cc code;
  We can do better than calling feof() for each character!

**********************************************************************/

45
#define MTEST_VERSION "2.4"
46

unknown's avatar
unknown committed
47
#include <my_global.h>
unknown's avatar
unknown committed
48
#include <mysql_embed.h>
49 50 51 52
#include <my_sys.h>
#include <m_string.h>
#include <mysql.h>
#include <mysql_version.h>
unknown's avatar
unknown committed
53
#include <mysqld_error.h>
54 55
#include <m_ctype.h>
#include <my_dir.h>
56
#include <errmsg.h>                       /* Error codes */
unknown's avatar
unknown committed
57
#include <hash.h>
58
#include <my_getopt.h>
unknown's avatar
unknown committed
59 60
#include <stdarg.h>
#include <sys/stat.h>
unknown's avatar
unknown committed
61
#include <violite.h>
62
#include <regex.h>                        /* Our own version of lib */
63
#define MAX_QUERY     131072
unknown's avatar
unknown committed
64
#define MAX_VAR_NAME	256
65
#define MAX_COLUMNS	256
66
#define PAD_SIZE	128
67
#define MAX_CONS	128
unknown's avatar
unknown committed
68
#define MAX_INCLUDE_DEPTH 16
unknown's avatar
unknown committed
69
#define LAZY_GUESS_BUF_SIZE 8192
70 71
#define INIT_Q_LINES	  1024
#define MIN_VAR_ALLOC	  32
72
#define BLOCK_STACK_DEPTH  32
73
#define MAX_EXPECTED_ERRORS 10
unknown's avatar
unknown committed
74 75
#define QUERY_SEND  1
#define QUERY_REAP  2
76 77 78
#ifndef MYSQL_MANAGER_PORT
#define MYSQL_MANAGER_PORT 23546
#endif
unknown's avatar
unknown committed
79
#define MAX_SERVER_ARGS 64
80

unknown's avatar
unknown committed
81 82 83 84 85 86 87
/*
  Sometimes in a test the client starts before
  the server - to solve the problem, we try again
  after some sleep if connection fails the first
  time
*/
#define CON_RETRY_SLEEP 2
88
#define MAX_CON_TRIES	5
unknown's avatar
unknown committed
89

90
#define SLAVE_POLL_INTERVAL 300000 /* 0.3 of a sec */
91 92
#define DEFAULT_DELIMITER ";"
#define MAX_DELIMITER 16
93

94
enum {OPT_MANAGER_USER=256,OPT_MANAGER_HOST,OPT_MANAGER_PASSWD,
unknown's avatar
unknown committed
95 96
      OPT_MANAGER_PORT,OPT_MANAGER_WAIT_TIMEOUT, OPT_SKIP_SAFEMALLOC,
      OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, OPT_SSL_CAPATH,
97
      OPT_SSL_CIPHER,OPT_PS_PROTOCOL};
unknown's avatar
unknown committed
98

99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
/* ************************************************************************ */
/*
  A line that starts with !$ or $S, and the list of error codes to
  --error are stored in an internal array of structs. This struct can
  hold numeric SQL error codes or SQLSTATE codes as strings. The
  element next to the last active element in the list is set to type
  ERR_EMPTY. When an SQL statement return an error we use this list to
  check if this  is an expected error.
*/
 
enum match_err_type
{
  ERR_EMPTY= 0,
  ERR_ERRNO,
  ERR_SQLSTATE
};

typedef struct
{
  enum match_err_type type;
  union
  {
    uint errnum;
    char sqlstate[SQLSTATE_LENGTH+1];  /* \0 terminated string */
  } code;
} match_err;

static match_err global_expected_errno[MAX_EXPECTED_ERRORS];
static uint global_expected_errors;

/* ************************************************************************ */

131
static int record = 0, opt_sleep=0;
unknown's avatar
unknown committed
132
static char *db = 0, *pass=0;
133
const char* user = 0, *host = 0, *unix_sock = 0, *opt_basedir="./";
134
static int port = 0;
135 136
static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0;
static my_bool tty_password= 0, ps_protocol= 0, ps_protocol_enabled= 0;
137
static uint start_lineno, *lineno;
138
const char* manager_user="root",*manager_host=0;
139 140 141 142
char *manager_pass=0;
int manager_port=MYSQL_MANAGER_PORT;
int manager_wait_timeout=3;
MYSQL_MANAGER* manager=0;
143

144
static char **default_argv;
unknown's avatar
unknown committed
145
static const char *load_default_groups[]= { "mysqltest","client",0 };
unknown's avatar
unknown committed
146
static char line_buffer[MAX_DELIMITER], *line_buffer_pos= line_buffer;
unknown's avatar
unknown committed
147

unknown's avatar
unknown committed
148 149 150 151 152
static FILE* file_stack[MAX_INCLUDE_DEPTH];
static FILE** cur_file;
static FILE** file_stack_end;
static uint lineno_stack[MAX_INCLUDE_DEPTH];
static char TMPDIR[FN_REFLEN];
153 154
static char delimiter[MAX_DELIMITER]= DEFAULT_DELIMITER;
static uint delimiter_length= 1;
unknown's avatar
unknown committed
155

unknown's avatar
unknown committed
156
static int *cur_block, *block_stack_end;
157 158 159
static int block_stack[BLOCK_STACK_DEPTH];

static int block_ok_stack[BLOCK_STACK_DEPTH];
unknown's avatar
unknown committed
160
static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */
161
static const char *charset_name= "latin1"; /* Default character set name */
162

163 164 165
static int embedded_server_arg_count=0;
static char *embedded_server_args[MAX_SERVER_ARGS];

166
static my_bool display_result_vertically= FALSE, display_metadata= FALSE;
167

unknown's avatar
unknown committed
168 169 170 171 172 173 174
/* See the timer_output() definition for details */
static char *timer_file = NULL;
static ulonglong timer_start;
static int got_end_timer= FALSE;
static void timer_output(void);
static ulonglong timer_now(void);

175 176 177 178 179 180
static regex_t ps_re; /* Holds precompiled re for valid PS statements */
static void ps_init_re(void);
static int ps_match_re(char *);
static char *ps_eprint(int);
static void ps_free_reg(void);

181 182 183 184 185 186 187
static const char *embedded_server_groups[] = {
  "server",
  "embedded",
  "mysqltest_SERVER",
  NullS
};

188 189
DYNAMIC_ARRAY q_lines;

unknown's avatar
unknown committed
190 191
#include "sslopt-vars.h"

192
typedef struct
unknown's avatar
unknown committed
193 194 195 196 197
{
  char file[FN_REFLEN];
  ulong pos;
} MASTER_POS ;

unknown's avatar
unknown committed
198 199 200 201 202 203
struct connection
{
  MYSQL mysql;
  char *name;
};

unknown's avatar
unknown committed
204 205 206 207
typedef struct
{
  int read_lines,current_line;
} PARSER;
208

209 210
MYSQL_RES *last_result=0;

211
PARSER parser;
unknown's avatar
unknown committed
212
MASTER_POS master_pos;
unknown's avatar
unknown committed
213
int *block_ok; /* set to 0 if the current block should not be executed */
214
int false_block_depth = 0;
unknown's avatar
unknown committed
215 216
/* if set, all results are concated and compared against this file */
const char *result_file = 0;
217

218
typedef struct
219
{
unknown's avatar
unknown committed
220
  char *name;
unknown's avatar
unknown committed
221
  int name_len;
unknown's avatar
unknown committed
222
  char *str_val;
223 224 225 226
  int str_val_len;
  int int_val;
  int alloced_len;
  int int_dirty; /* do not update string if int is updated until first read */
unknown's avatar
unknown committed
227
  int alloced;
228 229
} VAR;

unknown's avatar
unknown committed
230
#if defined(__NETWARE__) || defined(__WIN__)
unknown's avatar
unknown committed
231 232 233 234 235 236
/*
  Netware doesn't proved environment variable substitution that is done
  by the shell in unix environments. We do this in the following function:
*/

static char *subst_env_var(const char *cmd);
237
static FILE *my_popen(const char *cmd, const char *mode);
unknown's avatar
unknown committed
238 239 240
#define popen(A,B) my_popen((A),(B))
#endif /* __NETWARE__ */

241 242
VAR var_reg[10];
/*Perl/shell-like variable registers */
unknown's avatar
unknown committed
243
HASH var_hash;
244 245
my_bool disable_query_log=0, disable_result_log=0, disable_warnings=0;
my_bool disable_info= 1;			/* By default off */
246
my_bool abort_on_error= 1;
247

unknown's avatar
unknown committed
248 249 250
struct connection cons[MAX_CONS];
struct connection* cur_con, *next_con, *cons_end;

unknown's avatar
unknown committed
251 252 253
  /* Add new commands before Q_UNKNOWN !*/

enum enum_commands {
254
Q_CONNECTION=1,     Q_QUERY,
255
Q_CONNECT,	    Q_SLEEP, Q_REAL_SLEEP, 
256 257 258 259 260 261
Q_INC,		    Q_DEC,
Q_SOURCE,	    Q_DISCONNECT,
Q_LET,		    Q_ECHO,
Q_WHILE,	    Q_END_BLOCK,
Q_SYSTEM,	    Q_RESULT,
Q_REQUIRE,	    Q_SAVE_MASTER_POS,
262 263 264
Q_SYNC_WITH_MASTER,
Q_SYNC_SLAVE_WITH_MASTER,
Q_ERROR,
265
Q_SEND,		    Q_REAP,
266
Q_DIRTY_CLOSE,	    Q_REPLACE, Q_REPLACE_COLUMN,
267 268
Q_PING,		    Q_EVAL,
Q_RPL_PROBE,	    Q_ENABLE_RPL_PARSE,
269
Q_DISABLE_RPL_PARSE, Q_EVAL_RESULT,
unknown's avatar
unknown committed
270
Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
unknown's avatar
unknown committed
271
Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
272
Q_SERVER_START, Q_SERVER_STOP,Q_REQUIRE_MANAGER,
273
Q_WAIT_FOR_SLAVE_TO_STOP,
274 275
Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
Q_ENABLE_INFO, Q_DISABLE_INFO,
276
Q_ENABLE_METADATA, Q_DISABLE_METADATA,
277
Q_EXEC, Q_DELIMITER,
278
Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR,
279 280
Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL,
unknown's avatar
unknown committed
281
Q_START_TIMER, Q_END_TIMER,
282
Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL,
283

284 285
Q_UNKNOWN,			       /* Unknown command.   */
Q_COMMENT,			       /* Comments, ignored. */
unknown's avatar
unknown committed
286 287 288
Q_COMMENT_WITH_COMMAND
};

289
/* this should really be called command */
290
struct st_query
unknown's avatar
unknown committed
291
{
292
  char *query, *query_buf,*first_argument,*end;
unknown's avatar
unknown committed
293
  int first_word_len;
294
  my_bool abort_on_error, require_file;
295
  match_err expected_errno[MAX_EXPECTED_ERRORS];
unknown's avatar
unknown committed
296
  uint expected_errors;
unknown's avatar
unknown committed
297
  char record_file[FN_REFLEN];
unknown's avatar
unknown committed
298
  enum enum_commands type;
unknown's avatar
unknown committed
299 300
};

301 302 303 304 305
const char *command_names[]=
{
  "connection",
  "query",
  "connect",
unknown's avatar
unknown committed
306 307 308 309 310 311 312
  /* the difference between sleep and real_sleep is that sleep will use
     the delay from command line (--sleep) if there is one.
     real_sleep always uses delay from it's argument.
     the logic is that sometimes delays are cpu-dependent (and --sleep
     can be used to set this delay. real_sleep is used for cpu-independent
     delays
   */
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
  "sleep",
  "real_sleep",
  "inc",
  "dec",
  "source",
  "disconnect",
  "let",
  "echo",
  "while",
  "end",
  "system",
  "result",
  "require",
  "save_master_pos",
  "sync_with_master",
328
  "sync_slave_with_master",
329 330 331 332 333
  "error",
  "send",
  "reap",
  "dirty_close",
  "replace_result",
334
  "replace_column",
335 336 337 338 339 340 341 342 343 344 345 346 347 348
  "ping",
  "eval",
  "rpl_probe",
  "enable_rpl_parse",
  "disable_rpl_parse",
  "eval_result",
  "enable_query_log",
  "disable_query_log",
  "enable_result_log",
  "disable_result_log",
  "server_start",
  "server_stop",
  "require_manager",
  "wait_for_slave_to_stop",
349 350 351
  "enable_warnings",
  "disable_warnings",
  "enable_info",
352
  "disable_info",
353 354
  "enable_metadata",
  "disable_metadata",
355
  "exec",
356
  "delimiter",
357 358
  "disable_abort_on_error",
  "enable_abort_on_error",
359
  "vertical_results",
360
  "horizontal_results",
361
  "query_vertical",
362
  "query_horizontal",
unknown's avatar
unknown committed
363 364
  "start_timer",
  "end_timer",
unknown's avatar
unknown committed
365
  "character_set",
366 367
  "disable_ps_protocol",
  "enable_ps_protocol",
unknown's avatar
unknown committed
368
  0
369 370 371
};

TYPELIB command_typelib= {array_elements(command_names),"",
372
			  command_names, 0};
373

374
DYNAMIC_STRING ds_res;
unknown's avatar
unknown committed
375
static void die(const char *fmt, ...);
unknown's avatar
unknown committed
376
static void init_var_hash();
377
static VAR* var_from_env(const char *, const char *);
378
static byte* get_var_key(const byte* rec, uint* len, my_bool t);
unknown's avatar
unknown committed
379
static VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
unknown's avatar
unknown committed
380 381 382
		     int val_len);

static void var_free(void* v);
383

unknown's avatar
unknown committed
384 385
int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname);
void reject_dump(const char *record_file, char *buf, int size);
unknown's avatar
unknown committed
386

387
int close_connection(struct st_query* q);
unknown's avatar
unknown committed
388
static void set_charset(struct st_query*);
389 390
VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw,
	     my_bool ignore_not_existing);
unknown's avatar
unknown committed
391 392
int eval_expr(VAR* v, const char *p, const char** p_end);
static int read_server_arguments(const char *name);
393

394
/* Definitions for replace result */
unknown's avatar
unknown committed
395 396 397 398 399

typedef struct st_pointer_array {		/* when using array-strings */
  TYPELIB typelib;				/* Pointer to strings */
  byte	*str;					/* Strings is here */
  int7	*flag;					/* Flag about each var. */
400
  uint	array_allocs,max_count,length,max_length;
unknown's avatar
unknown committed
401 402 403 404 405 406
} POINTER_ARRAY;

struct st_replace;
struct st_replace *init_replace(my_string *from, my_string *to, uint count,
				my_string word_end_chars);
uint replace_strings(struct st_replace *rep, my_string *start,
unknown's avatar
unknown committed
407
		     uint *max_length, const char *from);
408
void free_replace();
unknown's avatar
unknown committed
409 410 411 412
static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name);
void free_pointer_array(POINTER_ARRAY *pa);
static int initialize_replace_buffer(void);
static void free_replace_buffer(void);
unknown's avatar
unknown committed
413 414 415
static void do_eval(DYNAMIC_STRING* query_eval, const char *query);
void str_to_file(const char *fname, char *str, int size);
int do_server_op(struct st_query* q,const char *op);
unknown's avatar
unknown committed
416 417 418 419

struct st_replace *glob_replace;
static char *out_buff;
static uint out_length;
420
static int eval_result = 0;
unknown's avatar
unknown committed
421

422 423 424 425 426 427 428
/* For column replace */
char *replace_column[MAX_COLUMNS];
uint max_replace_column= 0;

static void get_replace_column(struct st_query *q);
static void free_replace_column();

unknown's avatar
unknown committed
429
/* Disable functions that only exist in MySQL 4.0 */
unknown's avatar
SCRUM  
unknown committed
430
#if MYSQL_VERSION_ID < 40000
unknown's avatar
unknown committed
431 432 433
void mysql_enable_rpl_parse(MYSQL* mysql __attribute__((unused))) {}
void mysql_disable_rpl_parse(MYSQL* mysql __attribute__((unused))) {}
int mysql_rpl_parse_enabled(MYSQL* mysql __attribute__((unused))) { return 1; }
434
my_bool mysql_rpl_probe(MYSQL *mysql __attribute__((unused))) { return 1; }
unknown's avatar
unknown committed
435
#endif
436 437
static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
				      int len);
438

439 440 441 442 443 444
static void do_eval(DYNAMIC_STRING* query_eval, const char* query)
{
  const char* p;
  register char c;
  register int escaped = 0;
  VAR* v;
unknown's avatar
unknown committed
445
  DBUG_ENTER("do_eval");
unknown's avatar
unknown committed
446

447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
  for (p= query; (c = *p); ++p)
  {
    switch(c) {
    case '$':
      if (escaped)
      {
	escaped = 0;
	dynstr_append_mem(query_eval, p, 1);
      }
      else
      {
	if (!(v = var_get(p, &p, 0, 0)))
	  die("Bad variable in eval");
	dynstr_append_mem(query_eval, v->str_val, v->str_val_len);
      }
      break;
    case '\\':
      if (escaped)
      {
	escaped = 0;
	dynstr_append_mem(query_eval, p, 1);
      }
      else
	escaped = 1;
      break;
    default:
      dynstr_append_mem(query_eval, p, 1);
      break;
475
    }
476
  }
unknown's avatar
unknown committed
477
  DBUG_VOID_RETURN;
478
}
unknown's avatar
unknown committed
479

480

481 482
static void close_cons()
{
483
  DBUG_ENTER("close_cons");
484 485
  if (last_result)
    mysql_free_result(last_result);
486 487 488 489 490 491 492 493
  for (--next_con; next_con >= cons; --next_con)
  {
    mysql_close(&next_con->mysql);
    my_free(next_con->name, MYF(MY_ALLOW_ZERO_PTR));
  }
  DBUG_VOID_RETURN;
}

494

495 496
static void close_files()
{
unknown's avatar
unknown committed
497 498
  DBUG_ENTER("close_files");
  for (; cur_file != file_stack ; cur_file--)
499
  {
500
    if (*cur_file != stdin && *cur_file)
501
      my_fclose(*cur_file,MYF(0));
unknown's avatar
unknown committed
502 503
  }
  DBUG_VOID_RETURN;
504 505
}

506

507 508 509 510
static void free_used_memory()
{
  uint i;
  DBUG_ENTER("free_used_memory");
511
#ifndef EMBEDDED_LIBRARY
512 513
  if (manager)
    mysql_manager_close(manager);
514
#endif
515 516
  close_cons();
  close_files();
unknown's avatar
unknown committed
517
  hash_free(&var_hash);
unknown's avatar
unknown committed
518

519 520 521
  for (i=0 ; i < q_lines.elements ; i++)
  {
    struct st_query **q= dynamic_element(&q_lines, i, struct st_query**);
522
    my_free((gptr) (*q)->query_buf,MYF(MY_ALLOW_ZERO_PTR));
523 524
    my_free((gptr) (*q),MYF(0));
  }
525
  for (i=0; i < 10; i++)
unknown's avatar
unknown committed
526 527 528 529
  {
    if (var_reg[i].alloced_len)
      my_free(var_reg[i].str_val, MYF(MY_WME));
  }
530 531
  while (embedded_server_arg_count > 1)
    my_free(embedded_server_args[--embedded_server_arg_count],MYF(0));
532 533
  delete_dynamic(&q_lines);
  dynstr_free(&ds_res);
unknown's avatar
unknown committed
534
  free_replace();
535
  free_replace_column();
536 537
  my_free(pass,MYF(MY_ALLOW_ZERO_PTR));
  free_defaults(default_argv);
unknown's avatar
unknown committed
538
  mysql_server_end();
539 540
  if (ps_protocol)
    ps_free_reg();
541
  DBUG_VOID_RETURN;
542 543 544 545 546
}

static void die(const char* fmt, ...)
{
  va_list args;
547
  DBUG_ENTER("die");
548
  va_start(args, fmt);
549 550 551 552 553
  if (fmt)
  {
    fprintf(stderr, "%s: ", my_progname);
    vfprintf(stderr, fmt, args);
    fprintf(stderr, "\n");
unknown's avatar
unknown committed
554
    fflush(stderr);
555
  }
556
  va_end(args);
557
  free_used_memory();
unknown's avatar
unknown committed
558
  my_end(MY_CHECK_ERROR);
559 560 561
  exit(1);
}

562 563
/* Note that we will get some memory leaks when calling this! */

564 565
static void abort_not_supported_test()
{
566
  DBUG_ENTER("abort_not_supported_test");
567 568 569
  fprintf(stderr, "This test is not supported by this installation\n");
  if (!silent)
    printf("skipped\n");
570
  free_used_memory();
unknown's avatar
unknown committed
571
  my_end(MY_CHECK_ERROR);
572 573 574 575 576 577
  exit(2);
}

static void verbose_msg(const char* fmt, ...)
{
  va_list args;
578 579 580
  DBUG_ENTER("verbose_msg");
  if (!verbose)
    DBUG_VOID_RETURN;
581 582 583

  va_start(args, fmt);

584
  fprintf(stderr, "%s: At line %u: ", my_progname, start_lineno);
585 586 587
  vfprintf(stderr, fmt, args);
  fprintf(stderr, "\n");
  va_end(args);
588
  DBUG_VOID_RETURN;
589 590
}

unknown's avatar
unknown committed
591

592 593 594 595 596
void init_parser()
{
  parser.current_line = parser.read_lines = 0;
  memset(&var_reg,0, sizeof(var_reg));
}
unknown's avatar
unknown committed
597 598


599
int dyn_string_cmp(DYNAMIC_STRING* ds, const char* fname)
unknown's avatar
unknown committed
600 601
{
  MY_STAT stat_info;
602 603
  char *tmp, *res_ptr;
  char eval_file[FN_REFLEN];
unknown's avatar
unknown committed
604
  int res;
605
  uint res_len;
unknown's avatar
unknown committed
606
  int fd;
607
  DYNAMIC_STRING res_ds;
608 609
  DBUG_ENTER("dyn_string_cmp");

610 611 612 613 614 615 616 617 618
  if (!test_if_hard_path(fname))
  {
    strxmov(eval_file, opt_basedir, fname, NullS);
    fn_format(eval_file, eval_file,"","",4);
  }
  else
    fn_format(eval_file, fname,"","",4);

  if (!my_stat(eval_file, &stat_info, MYF(MY_WME)))
619
    die(NullS);
620 621 622 623
  if (!eval_result && (uint) stat_info.st_size != ds->length)
  {
    DBUG_PRINT("info",("Size differs:  result size: %u  file size: %u",
		       ds->length, stat_info.st_size));
624
    DBUG_RETURN(2);
625
  }
626
  if (!(tmp = (char*) my_malloc(stat_info.st_size + 1, MYF(MY_WME))))
627
    die(NullS);
628 629

  if ((fd = my_open(eval_file, O_RDONLY, MYF(MY_WME))) < 0)
630
    die(NullS);
631
  if (my_read(fd, (byte*)tmp, stat_info.st_size, MYF(MY_WME|MY_NABP)))
632
    die(NullS);
633 634 635 636 637 638
  tmp[stat_info.st_size] = 0;
  init_dynamic_string(&res_ds, "", 0, 65536);
  if (eval_result)
  {
    do_eval(&res_ds, tmp);
    res_ptr = res_ds.str;
639
    if ((res_len = res_ds.length) != ds->length)
640 641 642 643 644 645 646 647 648 649
    {
      res = 2;
      goto err;
    }
  }
  else
  {
    res_ptr = tmp;
    res_len = stat_info.st_size;
  }
unknown's avatar
unknown committed
650

651
  res = (memcmp(res_ptr, ds->str, res_len)) ?  1 : 0;
unknown's avatar
unknown committed
652

653 654 655 656
err:
  if (res && eval_result)
    str_to_file(fn_format(eval_file, fname, "", ".eval",2), res_ptr,
		res_len);
unknown's avatar
unknown committed
657

658 659
  my_free((gptr) tmp, MYF(0));
  my_close(fd, MYF(MY_WME));
660
  dynstr_free(&res_ds);
unknown's avatar
unknown committed
661

662
  DBUG_RETURN(res);
unknown's avatar
unknown committed
663 664
}

665
static int check_result(DYNAMIC_STRING* ds, const char* fname,
666
			my_bool require_option)
unknown's avatar
unknown committed
667 668
{
  int error = 0;
669 670 671 672
  int res=dyn_string_cmp(ds, fname);

  if (res && require_option)
    abort_not_supported_test();
673
  switch (res) {
674 675 676 677 678 679 680 681 682 683 684 685 686 687
  case 0:
    break; /* ok */
  case 2:
    verbose_msg("Result length mismatch");
    error = 1;
    break;
  case 1:
    verbose_msg("Result content mismatch");
    error = 1;
    break;
  default: /* impossible */
    die("Unknown error code from dyn_string_cmp()");
  }
  if (error)
688
    reject_dump(fname, ds->str, ds->length);
unknown's avatar
unknown committed
689 690 691
  return error;
}

692

693 694
VAR* var_get(const char* var_name, const char** var_name_end, my_bool raw,
	     my_bool ignore_not_existing)
695 696 697
{
  int digit;
  VAR* v;
698 699 700 701
  DBUG_ENTER("var_get");
  DBUG_PRINT("enter",("var_name: %s",var_name));

  if (*var_name != '$')
702
    goto err;
703
  digit = *++var_name - '0';
unknown's avatar
unknown committed
704
  if (digit < 0 || digit >= 10)
705
  {
unknown's avatar
unknown committed
706
    const char* save_var_name = var_name, *end;
707
    uint length;
unknown's avatar
unknown committed
708
    end = (var_name_end) ? *var_name_end : 0;
709
    while (my_isvar(charset_info,*var_name) && var_name != end)
710
      var_name++;
711 712 713 714
    if (var_name == save_var_name)
    {
      if (ignore_not_existing)
	DBUG_RETURN(0);
unknown's avatar
unknown committed
715
      die("Empty variable");
716
    }
717
    length= (uint) (var_name - save_var_name);
718 719
    if (length >= MAX_VAR_NAME)
      die("Too long variable name: %s", save_var_name);
unknown's avatar
unknown committed
720

721
    if (!(v = (VAR*) hash_search(&var_hash, save_var_name, length)))
722
    {
723
      char buff[MAX_VAR_NAME+1];
unknown's avatar
unknown committed
724
      strmake(buff, save_var_name, length);
725
      v= var_from_env(buff, "");
726
    }
727
    var_name--;					/* Point at last character */
728
  }
729
  else
730
    v = var_reg + digit;
unknown's avatar
unknown committed
731

732 733 734 735
  if (!raw && v->int_dirty)
  {
    sprintf(v->str_val, "%d", v->int_val);
    v->int_dirty = 0;
736
    v->str_val_len = strlen(v->str_val);
737
  }
738
  if (var_name_end)
739
    *var_name_end = var_name  ;
740
  DBUG_RETURN(v);
741 742
err:
  if (var_name_end)
743 744
    *var_name_end = 0;
  die("Unsupported variable name: %s", var_name);
745
  DBUG_RETURN(0);
746 747
}

748
static VAR *var_obtain(const char* name, int len)
unknown's avatar
unknown committed
749 750
{
  VAR* v;
751
  if ((v = (VAR*)hash_search(&var_hash, name, len)))
unknown's avatar
unknown committed
752
    return v;
753
  v = var_init(0, name, len, "", 0);
unknown's avatar
SCRUM  
unknown committed
754
  my_hash_insert(&var_hash, (byte*)v);
unknown's avatar
unknown committed
755 756 757
  return v;
}

758 759
int var_set(const char *var_name, const char *var_name_end,
            const char *var_val, const char *var_val_end)
760 761 762
{
  int digit;
  VAR* v;
763 764 765 766 767 768
  DBUG_ENTER("var_set");
  DBUG_PRINT("enter", ("var_name: '%.*s' = '%.*s' (length: %d)",
                       (int) (var_name_end - var_name), var_name,
                       (int) (var_val_end - var_val), var_val,
                       (int) (var_val_end - var_val)));

769
  if (*var_name++ != '$')
770 771 772 773
  {
    var_name--;
    die("Variable name in %s does not start with '$'", var_name);
  }
774
  digit = *var_name - '0';
775
  if (!(digit < 10 && digit >= 0))
776 777 778
  {
    v = var_obtain(var_name, (uint) (var_name_end - var_name));
  }
779
  else
780
    v = var_reg + digit;
781
  return eval_expr(v, var_val, (const char**)&var_val_end);
782 783
}

784

unknown's avatar
unknown committed
785 786
int open_file(const char* name)
{
787 788 789 790 791 792 793 794
  char buff[FN_REFLEN];
  if (!test_if_hard_path(name))
  {
    strxmov(buff, opt_basedir, name, NullS);
    name=buff;
  }
  fn_format(buff,name,"","",4);

795
  if (*cur_file && cur_file == file_stack_end)
unknown's avatar
unknown committed
796
    die("Source directives are nesting too deep");
unknown's avatar
unknown committed
797
  if (!(*(cur_file+1) = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
798 799
    die(NullS);
  cur_file++;
800
  *++lineno=1;
801

unknown's avatar
unknown committed
802 803 804
  return 0;
}

unknown's avatar
unknown committed
805

806
/* ugly long name, but we are following the convention */
unknown's avatar
unknown committed
807
int do_wait_for_slave_to_stop(struct st_query* q __attribute__((unused)))
808 809 810 811
{
  MYSQL* mysql = &cur_con->mysql;
  for (;;)
  {
unknown's avatar
unknown committed
812
    MYSQL_RES *res;
813 814 815
    MYSQL_ROW row;
    int done;
    LINT_INIT(res);
816

unknown's avatar
unknown committed
817 818
    if (mysql_query(mysql,"show status like 'Slave_running'") ||
	!(res=mysql_store_result(mysql)))
819 820 821 822 823 824 825 826 827 828 829
      die("Query failed while probing slave for stop: %s",
	  mysql_error(mysql));
    if (!(row=mysql_fetch_row(res)) || !row[1])
    {
      mysql_free_result(res);
      die("Strange result from query while probing slave for stop");
    }
    done = !strcmp(row[1],"OFF");
    mysql_free_result(res);
    if (done)
      break;
unknown's avatar
unknown committed
830
    my_sleep(SLAVE_POLL_INTERVAL);
831 832 833 834
  }
  return 0;
}

unknown's avatar
unknown committed
835
int do_require_manager(struct st_query* a __attribute__((unused)))
836 837 838 839 840 841
{
  if (!manager)
    abort_not_supported_test();
  return 0;
}

842
#ifndef EMBEDDED_LIBRARY
843 844 845 846 847 848 849 850 851 852 853 854 855 856
int do_server_start(struct st_query* q)
{
  return do_server_op(q,"start");
}

int do_server_stop(struct st_query* q)
{
  return do_server_op(q,"stop");
}

int do_server_op(struct st_query* q,const char* op)
{
  char* p=q->first_argument;
  char com_buf[256],*com_p;
857 858 859 860
  if (!manager)
  {
    die("Manager is not initialized, manager commands are not possible");
  }
861 862 863 864
  com_p=strmov(com_buf,op);
  com_p=strmov(com_p,"_exec ");
  if (!*p)
    die("Missing server name in server_%s\n",op);
865
  while (*p && !my_isspace(charset_info,*p))
unknown's avatar
unknown committed
866
   *com_p++= *p++;
867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
  *com_p++=' ';
  com_p=int10_to_str(manager_wait_timeout,com_p,10);
  *com_p++ = '\n';
  *com_p=0;
  if (mysql_manager_command(manager,com_buf,(int)(com_p-com_buf)))
    die("Error in command: %s(%d)",manager->last_error,manager->last_errno);
  while (!manager->eof)
  {
    if (mysql_manager_fetch_line(manager,com_buf,sizeof(com_buf)))
      die("Error fetching result line: %s(%d)", manager->last_error,
	  manager->last_errno);
  }

  return 0;
}
882
#endif
883

884
int do_source(struct st_query* q)
unknown's avatar
unknown committed
885
{
886
  char* p=q->first_argument, *name;
887
  if (!*p)
unknown's avatar
unknown committed
888 889
    die("Missing file name in source\n");
  name = p;
890
  while (*p && !my_isspace(charset_info,*p))
unknown's avatar
unknown committed
891 892
    p++;
  *p = 0;
893

unknown's avatar
unknown committed
894 895 896
  return open_file(name);
}

897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912
/*
  Execute given command.

  SYNOPSIS
    do_exec()
    q	called command

  DESCRIPTION
    If one uses --exec command [args] command in .test file
    we will execute the command and record its output.

  RETURN VALUES
    0	ok
    1	error
*/

unknown's avatar
unknown committed
913
static void do_exec(struct st_query* q)
914
{
unknown's avatar
unknown committed
915
  int error;
unknown's avatar
unknown committed
916
  DYNAMIC_STRING *ds= NULL;			/* Assign just to avoid warning */
917 918 919 920
  DYNAMIC_STRING ds_tmp;
  char buf[1024];
  FILE *res_file;
  char *cmd= q->first_argument;
unknown's avatar
unknown committed
921
  DBUG_ENTER("do_exec");
922

unknown's avatar
unknown committed
923
  while (*cmd && my_isspace(charset_info, *cmd))
924 925 926 927
    cmd++;
  if (!*cmd)
    die("Missing argument in exec\n");

unknown's avatar
unknown committed
928 929 930 931 932
  DBUG_PRINT("info", ("Executing '%s'", cmd));

  if (!(res_file= popen(cmd, "r")) && q->abort_on_error)
    die("popen() failed\n");

933
  if (disable_result_log)
934
  {
unknown's avatar
unknown committed
935
    while (fgets(buf, sizeof(buf), res_file))
936 937 938 939
    {
      buf[strlen(buf)-1]=0;
      DBUG_PRINT("exec_result",("%s", buf));
    }
940 941
  }
  else
942 943 944 945 946 947 948 949
  {
    if (q->record_file[0])
    {
      init_dynamic_string(&ds_tmp, "", 16384, 65536);
      ds= &ds_tmp;
    }
    else
      ds= &ds_res;
950

951 952
    while (fgets(buf, sizeof(buf), res_file))
      replace_dynstr_append_mem(ds, buf, strlen(buf));
unknown's avatar
unknown committed
953 954 955 956
  }
  error= pclose(res_file);

  if (error != 0)
unknown's avatar
unknown committed
957
    die("command \"%s\" failed", cmd);
unknown's avatar
unknown committed
958

unknown's avatar
unknown committed
959 960
  if (!disable_result_log)
  {
961 962
    if (glob_replace)
      free_replace();
963

964 965 966 967 968 969 970 971 972 973 974 975 976
    if (record)
    {
      if (!q->record_file[0] && !result_file)
        die("At line %u: Missing result file", start_lineno);
      if (!result_file)
        str_to_file(q->record_file, ds->str, ds->length);
    }
    else if (q->record_file[0])
    {
      error= check_result(ds, q->record_file, q->require_file);
    }
    if (ds == &ds_tmp)
      dynstr_free(&ds_tmp);
977 978 979
  }
}

unknown's avatar
unknown committed
980

981 982 983 984 985 986 987
int var_query_set(VAR* v, const char* p, const char** p_end)
{
  char* end = (char*)((p_end && *p_end) ? *p_end : p + strlen(p));
  MYSQL_RES *res;
  MYSQL_ROW row;
  MYSQL* mysql = &cur_con->mysql;
  LINT_INIT(res);
unknown's avatar
unknown committed
988

989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002
  while (end > p && *end != '`')
    --end;
  if (p == end)
    die("Syntax error in query, missing '`'");
  ++p;

  if (mysql_real_query(mysql, p, (int)(end - p)) ||
      !(res = mysql_store_result(mysql)))
  {
    *end = 0;
    die("Error running query '%s': %s", p, mysql_error(mysql));
  }

  if ((row = mysql_fetch_row(res)) && row[0])
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
  {
    /*
      Concatenate all row results with tab in between to allow us to work
      with results from many columns (for example from SHOW VARIABLES)
    */
    DYNAMIC_STRING result;
    uint i;
    ulong *lengths;
    char *end;

    init_dynamic_string(&result, "", 16384, 65536);
    lengths= mysql_fetch_lengths(res);
    for (i=0; i < mysql_num_fields(res); i++)
    {
      if (row[0])
	dynstr_append_mem(&result, row[i], lengths[i]);
      dynstr_append_mem(&result, "\t", 1);
    }
    end= result.str + result.length-1;
    eval_expr(v, result.str, (const char**) &end);
    dynstr_free(&result);
  }
1025 1026 1027 1028 1029 1030
  else
    eval_expr(v, "", 0);

  mysql_free_result(res);
  return 0;
}
1031

unknown's avatar
unknown committed
1032 1033 1034 1035 1036
void var_copy(VAR* dest, VAR* src)
{
  dest->int_val=src->int_val;
  dest->int_dirty=src->int_dirty;
  if (dest->alloced_len < src->alloced_len &&
1037
      !(dest->str_val=my_realloc(dest->str_val,src->alloced_len+1,
unknown's avatar
unknown committed
1038 1039 1040
				 MYF(MY_WME))))
    die("Out of memory");
  dest->str_val_len=src->str_val_len;
1041
  memcpy(dest->str_val,src->str_val,src->str_val_len+1);
unknown's avatar
unknown committed
1042 1043
}

1044
int eval_expr(VAR* v, const char* p, const char** p_end)
1045 1046
{
  VAR* vp;
1047
  if (*p == '$')
1048 1049
  {
    if ((vp = var_get(p,p_end,0,0)))
1050
    {
1051 1052
      var_copy(v, vp);
      return 0;
unknown's avatar
unknown committed
1053
    }
1054
  }
1055
  else if (*p == '`')
1056 1057 1058
  {
    return var_query_set(v, p, p_end);
  }
unknown's avatar
unknown committed
1059 1060
  else
    {
1061 1062 1063 1064
      int new_val_len = (p_end && *p_end) ?
	 (int) (*p_end - p) : (int) strlen(p);
      if (new_val_len + 1 >= v->alloced_len)
      {
1065
	v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
1066 1067
	  MIN_VAR_ALLOC : new_val_len + 1;
	if (!(v->str_val =
1068
	      v->str_val ? my_realloc(v->str_val, v->alloced_len+1,
1069
				      MYF(MY_WME)) :
1070
	      my_malloc(v->alloced_len+1, MYF(MY_WME))))
1071 1072 1073 1074 1075
	  die("Out of memory");
      }
      v->str_val_len = new_val_len;
      memcpy(v->str_val, p, new_val_len);
      v->str_val[new_val_len] = 0;
unknown's avatar
unknown committed
1076 1077
      v->int_val=atoi(p);
      v->int_dirty=0;
1078 1079
      return 0;
    }
1080

1081 1082 1083 1084
  die("Invalid expr: %s", p);
  return 1;
}

1085
int do_inc(struct st_query* q)
1086
{
1087
  char* p=q->first_argument;
1088
  VAR* v;
1089
  v = var_get(p, 0, 1, 0);
1090 1091 1092 1093 1094
  v->int_val++;
  v->int_dirty = 1;
  return 0;
}

1095
int do_dec(struct st_query* q)
1096
{
1097
  char* p=q->first_argument;
1098
  VAR* v;
1099
  v = var_get(p, 0, 1, 0);
1100 1101 1102 1103 1104
  v->int_val--;
  v->int_dirty = 1;
  return 0;
}

1105
int do_system(struct st_query* q)
unknown's avatar
unknown committed
1106
{
1107
  char* p=q->first_argument;
unknown's avatar
unknown committed
1108
  VAR v;
1109
  var_init(&v, 0, 0, 0, 0);
unknown's avatar
unknown committed
1110
  eval_expr(&v, p, 0); /* NULL terminated */
unknown's avatar
unknown committed
1111
  if (v.str_val_len)
unknown's avatar
unknown committed
1112
  {
unknown's avatar
unknown committed
1113
    char expr_buf[1024];
unknown's avatar
unknown committed
1114 1115 1116 1117 1118 1119 1120 1121
    if ((uint)v.str_val_len > sizeof(expr_buf) - 1)
      v.str_val_len = sizeof(expr_buf) - 1;
    memcpy(expr_buf, v.str_val, v.str_val_len);
    expr_buf[v.str_val_len] = 0;
    DBUG_PRINT("info", ("running system command '%s'", expr_buf));
    if (system(expr_buf) && q->abort_on_error)
      die("system command '%s' failed", expr_buf);
  }
unknown's avatar
unknown committed
1122
  var_free(&v);
unknown's avatar
unknown committed
1123 1124
  return 0;
}
1125

1126
int do_echo(struct st_query* q)
1127
{
1128
  char* p=q->first_argument;
1129
  VAR v;
1130
  var_init(&v,0,0,0,0);
1131
  eval_expr(&v, p, 0); /* NULL terminated */
unknown's avatar
unknown committed
1132 1133 1134 1135 1136
  if (v.str_val_len)
  {
    fflush(stdout);
    write(1, v.str_val, v.str_val_len);
  }
1137
  write(1, "\n", 1);
unknown's avatar
unknown committed
1138
  var_free(&v);
1139 1140 1141
  return 0;
}

1142

1143
int do_sync_with_master2(const char* p)
unknown's avatar
unknown committed
1144 1145 1146 1147 1148
{
  MYSQL_RES* res;
  MYSQL_ROW row;
  MYSQL* mysql = &cur_con->mysql;
  char query_buf[FN_REFLEN+128];
1149
  int offset= 0, tries= 0;
1150 1151
  int rpl_parse;

1152 1153 1154 1155
  if (!master_pos.file[0])
  {
    die("Line %u: Calling 'sync_with_master' without calling 'save_master_pos'", start_lineno);
  }
1156 1157
  rpl_parse = mysql_rpl_parse_enabled(mysql);
  mysql_disable_rpl_parse(mysql);
unknown's avatar
unknown committed
1158

1159
  if (*p)
1160
    offset = atoi(p);
unknown's avatar
unknown committed
1161

unknown's avatar
unknown committed
1162
  sprintf(query_buf, "select master_pos_wait('%s', %ld)", master_pos.file,
1163
	  master_pos.pos + offset);
1164 1165 1166

wait_for_position:

1167 1168
  if (mysql_query(mysql, query_buf))
    die("line %u: failed in %s: %d: %s", start_lineno, query_buf,
unknown's avatar
unknown committed
1169 1170
	mysql_errno(mysql), mysql_error(mysql));

1171
  if (!(last_result = res = mysql_store_result(mysql)))
1172 1173
    die("line %u: mysql_store_result() returned NULL for '%s'", start_lineno,
	query_buf);
1174
  if (!(row = mysql_fetch_row(res)))
unknown's avatar
unknown committed
1175
    die("line %u: empty result in %s", start_lineno, query_buf);
1176
  if (!row[0])
1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
  {
    /*
      It may be that the slave SQL thread has not started yet, though START
      SLAVE has been issued ?
    */
    if (tries++ == 3)
    {
      die("line %u: could not sync with master ('%s' returned NULL)", 
          start_lineno, query_buf);
    }
    sleep(1); /* So at most we will wait 3 seconds and make 4 tries */
    mysql_free_result(res);
    goto wait_for_position;
  }
unknown's avatar
unknown committed
1191 1192
  mysql_free_result(res);
  last_result=0;
1193
  if (rpl_parse)
1194
    mysql_enable_rpl_parse(mysql);
unknown's avatar
unknown committed
1195

unknown's avatar
unknown committed
1196 1197 1198
  return 0;
}

1199 1200 1201 1202
int do_sync_with_master(struct st_query* q)
{
  return do_sync_with_master2(q->first_argument);
}
1203

unknown's avatar
unknown committed
1204 1205 1206 1207 1208
int do_save_master_pos()
{
  MYSQL_RES* res;
  MYSQL_ROW row;
  MYSQL* mysql = &cur_con->mysql;
1209
  const char *query;
1210 1211 1212 1213
  int rpl_parse;

  rpl_parse = mysql_rpl_parse_enabled(mysql);
  mysql_disable_rpl_parse(mysql);
unknown's avatar
unknown committed
1214

1215
  if (mysql_query(mysql, query= "show master status"))
unknown's avatar
unknown committed
1216 1217 1218
    die("At line %u: failed in show master status: %d: %s", start_lineno,
	mysql_errno(mysql), mysql_error(mysql));

1219
  if (!(last_result =res = mysql_store_result(mysql)))
1220 1221
    die("line %u: mysql_store_result() retuned NULL for '%s'", start_lineno,
	query);
1222
  if (!(row = mysql_fetch_row(res)))
unknown's avatar
unknown committed
1223
    die("line %u: empty result in show master status", start_lineno);
1224 1225
  strnmov(master_pos.file, row[0], sizeof(master_pos.file)-1);
  master_pos.pos = strtoul(row[1], (char**) 0, 10);
1226
  mysql_free_result(res); last_result=0;
unknown's avatar
unknown committed
1227

1228
  if (rpl_parse)
1229
    mysql_enable_rpl_parse(mysql);
unknown's avatar
unknown committed
1230

unknown's avatar
unknown committed
1231 1232 1233 1234
  return 0;
}


1235
int do_let(struct st_query* q)
1236
{
1237 1238
  char* p=q->first_argument;
  char *var_name, *var_name_end, *var_val_start;
1239
  if (!*p)
1240 1241
    die("Missing variable name in let\n");
  var_name = p;
1242
  while (*p && (*p != '=' || my_isspace(charset_info,*p)))
1243 1244
    p++;
  var_name_end = p;
1245
  if (*p == '=') p++;
1246
  while (*p && my_isspace(charset_info,*p))
1247 1248
    p++;
  var_val_start = p;
1249
  return var_set(var_name, var_name_end, var_val_start, q->end);
1250 1251
}

1252 1253 1254 1255 1256 1257 1258 1259

/*
  Store an integer (typically the returncode of the last SQL)
  statement in the mysqltest builtin variable $mysql_errno, by
  simulating of a user statement "let $mysql_errno= <integer>"
*/

int var_set_errno(int sql_errno)
1260
{
1261 1262 1263 1264
  const char *var_name= "$mysql_errno";
  char var_val[21];
  uint length= my_sprintf(var_val, (var_val, "%d", sql_errno));
  return var_set(var_name, var_name + 12, var_val, var_val + length);
1265 1266
}

1267

unknown's avatar
unknown committed
1268
int do_rpl_probe(struct st_query* q __attribute__((unused)))
1269
{
unknown's avatar
unknown committed
1270
  DBUG_ENTER("do_rpl_probe");
1271
  if (mysql_rpl_probe(&cur_con->mysql))
unknown's avatar
unknown committed
1272 1273
    die("Failed in mysql_rpl_probe(): '%s'", mysql_error(&cur_con->mysql));
  DBUG_RETURN(0);
1274 1275
}

1276

unknown's avatar
unknown committed
1277
int do_enable_rpl_parse(struct st_query* q __attribute__((unused)))
1278 1279 1280 1281 1282
{
  mysql_enable_rpl_parse(&cur_con->mysql);
  return 0;
}

1283

unknown's avatar
unknown committed
1284
int do_disable_rpl_parse(struct st_query* q __attribute__((unused)))
1285 1286 1287 1288 1289 1290
{
  mysql_disable_rpl_parse(&cur_con->mysql);
  return 0;
}


1291
int do_sleep(struct st_query* q, my_bool real_sleep)
unknown's avatar
unknown committed
1292
{
unknown's avatar
unknown committed
1293
  char *p=q->first_argument;
1294
  while (*p && my_isspace(charset_info,*p))
unknown's avatar
unknown committed
1295
    p++;
1296 1297
  if (!*p)
    die("Missing argument in sleep\n");
1298
  if (opt_sleep && !real_sleep)
unknown's avatar
unknown committed
1299
    my_sleep(opt_sleep * 1000000L);
unknown's avatar
unknown committed
1300
  else
unknown's avatar
unknown committed
1301
    my_sleep((ulong) (atof(p) * 1000000L));
unknown's avatar
unknown committed
1302
  return 0;
unknown's avatar
unknown committed
1303 1304
}

1305
static void get_file_name(char *filename, struct st_query* q)
1306
{
1307
  char* p=q->first_argument;
1308 1309
  strnmov(filename, p, FN_REFLEN);
  /* Remove end space */
1310
  while (p > filename && my_isspace(charset_info,p[-1]))
1311 1312 1313 1314
    p--;
  p[0]=0;
}

unknown's avatar
unknown committed
1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331
static void set_charset(struct st_query* q)
{
  char* charset_name= q->first_argument;
  char* tmp;

  if (!charset_name || !*charset_name)
    die("Missing charset name in 'character_set'\n");
  /* Remove end space */
  tmp= charset_name;
  while (*tmp && !my_isspace(charset_info,*tmp))
    tmp++;
  *tmp= 0;

  charset_info= get_charset_by_csname(charset_name,MY_CS_PRIMARY,MYF(MY_WME));
  if (!charset_info)
    abort_not_supported_test();
}
unknown's avatar
unknown committed
1332

1333
static uint get_errcodes(match_err *to,struct st_query* q)
unknown's avatar
unknown committed
1334
{
1335 1336 1337
  char* p= q->first_argument;
  uint count= 0;
  DBUG_ENTER("get_errcodes");
1338

unknown's avatar
unknown committed
1339 1340
  if (!*p)
    die("Missing argument in %s\n", q->query);
1341

1342
  do
1343
  {
1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362
    if (*p == 'S')
    {
      /* SQLSTATE string */
      int i;
      p++;
      for (i = 0; my_isalnum(charset_info, *p) && i < SQLSTATE_LENGTH; p++, i++)
        to[count].code.sqlstate[i]= *p;
      to[count].code.sqlstate[i]= '\0';
      to[count].type= ERR_SQLSTATE;
    }
    else
    {
      long val;
      p=str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val);
      if (p == NULL)
        die("Invalid argument in %s\n", q->query);
      to[count].code.errnum= (uint) val;
      to[count].type= ERR_ERRNO;
    }
unknown's avatar
unknown committed
1363
    count++;
1364 1365 1366
  } while (*(p++) == ',');

  to[count].type= ERR_EMPTY;                        /* End of data */
unknown's avatar
unknown committed
1367
  DBUG_RETURN(count);
unknown's avatar
unknown committed
1368 1369
}

unknown's avatar
unknown committed
1370 1371 1372
/*
  Get a string;  Return ptr to end of string
  Strings may be surrounded by " or '
1373 1374

  If string is a '$variable', return the value of the variable.
unknown's avatar
unknown committed
1375 1376 1377
*/


1378 1379
static char *get_string(char **to_ptr, char **from_ptr,
			struct st_query* q)
unknown's avatar
unknown committed
1380 1381
{
  reg1 char c,sep;
1382
  char *to= *to_ptr, *from= *from_ptr, *start=to;
unknown's avatar
unknown committed
1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428
  DBUG_ENTER("get_string");

  /* Find separator */
  if (*from == '"' || *from == '\'')
    sep= *from++;
  else
    sep=' ';				/* Separated with space */

  for ( ; (c=*from) ; from++)
  {
    if (c == '\\' && from[1])
    {					/* Escaped character */
      /* We can't translate \0 -> ASCII 0 as replace can't handle ASCII 0 */
      switch (*++from) {
      case 'n':
	*to++= '\n';
	break;
      case 't':
	*to++= '\t';
	break;
      case 'r':
	*to++ = '\r';
	break;
      case 'b':
	*to++ = '\b';
	break;
      case 'Z':				/* ^Z must be escaped on Win32 */
	*to++='\032';
	break;
      default:
	*to++ = *from;
	break;
      }
    }
    else if (c == sep)
    {
      if (c == ' ' || c != *++from)
	break;				/* Found end of string */
      *to++=c;				/* Copy duplicated separator */
    }
    else
      *to++=c;
  }
  if (*from != ' ' && *from)
    die("Wrong string argument in %s\n", q->query);

1429
  while (my_isspace(charset_info,*from))	/* Point to next string */
unknown's avatar
unknown committed
1430 1431
    from++;

1432 1433
  *to =0;				/* End of string marker */
  *to_ptr= to+1;			/* Store pointer to end */
unknown's avatar
unknown committed
1434
  *from_ptr= from;
1435 1436 1437 1438 1439 1440 1441 1442

  /* Check if this was a variable */
  if (*start == '$')
  {
    const char *end= to;
    VAR *var=var_get(start, &end, 0, 1);
    if (var && to == (char*) end+1)
    {
1443
      DBUG_PRINT("info",("var: '%s' -> '%s'", start, var->str_val));
1444 1445 1446 1447
      DBUG_RETURN(var->str_val);	/* return found variable value */
    }
  }
  DBUG_RETURN(start);
unknown's avatar
unknown committed
1448 1449 1450 1451 1452 1453 1454
}


/*
  Get arguments for replace. The syntax is:
  replace from to [from to ...]
  Where each argument may be quoted with ' or "
1455 1456
  A argument may also be a variable, in which case the value of the
  variable is replaced.
unknown's avatar
unknown committed
1457 1458 1459 1460 1461 1462
*/

static void get_replace(struct st_query *q)
{
  uint i;
  char *from=q->first_argument;
1463
  char *buff,*start;
unknown's avatar
unknown committed
1464 1465 1466 1467
  char word_end_chars[256],*pos;
  POINTER_ARRAY to_array,from_array;
  DBUG_ENTER("get_replace");

unknown's avatar
unknown committed
1468
  free_replace();
1469

unknown's avatar
unknown committed
1470 1471 1472 1473
  bzero((char*) &to_array,sizeof(to_array));
  bzero((char*) &from_array,sizeof(from_array));
  if (!*from)
    die("Missing argument in %s\n", q->query);
1474
  start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
unknown's avatar
unknown committed
1475 1476 1477
  while (*from)
  {
    char *to=buff;
1478
    to=get_string(&buff, &from, q);
unknown's avatar
unknown committed
1479
    if (!*from)
1480
      die("Wrong number of arguments to replace in %s\n", q->query);
unknown's avatar
unknown committed
1481
    insert_pointer_name(&from_array,to);
1482
    to=get_string(&buff, &from, q);
unknown's avatar
unknown committed
1483 1484 1485
    insert_pointer_name(&to_array,to);
  }
  for (i=1,pos=word_end_chars ; i < 256 ; i++)
1486
    if (my_isspace(charset_info,i))
unknown's avatar
unknown committed
1487
      *pos++= i;
1488
  *pos=0;					/* End pointer */
unknown's avatar
unknown committed
1489 1490 1491 1492 1493 1494 1495 1496
  if (!(glob_replace=init_replace((char**) from_array.typelib.type_names,
				  (char**) to_array.typelib.type_names,
				  (uint) from_array.typelib.count,
				  word_end_chars)) ||
      initialize_replace_buffer())
    die("Can't initialize replace from %s\n", q->query);
  free_pointer_array(&from_array);
  free_pointer_array(&to_array);
1497
  my_free(start, MYF(0));
1498
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1499 1500 1501 1502
}

void free_replace()
{
1503
  DBUG_ENTER("free_replace");
unknown's avatar
unknown committed
1504 1505 1506 1507 1508 1509
  if (glob_replace)
  {
    my_free((char*) glob_replace,MYF(0));
    glob_replace=0;
    free_replace_buffer();
  }
1510
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1511 1512
}

1513
int select_connection(char *p)
unknown's avatar
unknown committed
1514
{
1515
  char* name;
unknown's avatar
unknown committed
1516
  struct connection *con;
1517 1518 1519
  DBUG_ENTER("select_connection");
  DBUG_PRINT("enter",("name: '%s'",p));

1520
  if (!*p)
unknown's avatar
unknown committed
1521 1522
    die("Missing connection name in connect\n");
  name = p;
1523
  while (*p && !my_isspace(charset_info,*p))
unknown's avatar
unknown committed
1524 1525
    p++;
  *p = 0;
1526

1527 1528
  for (con = cons; con < next_con; con++)
  {
1529
    if (!strcmp(con->name, name))
1530 1531 1532 1533 1534
    {
      cur_con = con;
      DBUG_RETURN(0);
    }
  }
unknown's avatar
unknown committed
1535
  die("connection '%s' not found in connection pool", name);
1536
  DBUG_RETURN(1);				/* Never reached */
unknown's avatar
unknown committed
1537 1538
}

1539
int close_connection(struct st_query* q)
1540
{
1541
  char* p=q->first_argument, *name;
1542
  struct connection *con;
1543 1544 1545
  DBUG_ENTER("close_connection");
  DBUG_PRINT("enter",("name: '%s'",p));

1546
  if (!*p)
1547 1548
    die("Missing connection name in connect\n");
  name = p;
1549
  while (*p && !my_isspace(charset_info,*p))
1550 1551
    p++;
  *p = 0;
1552

unknown's avatar
unknown committed
1553
  for (con = cons; con < next_con; con++)
1554
  {
1555
    if (!strcmp(con->name, name))
1556
    {
1557
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
1558 1559 1560
      if (q->type == Q_DIRTY_CLOSE)
      {
	if (con->mysql.net.vio)
unknown's avatar
unknown committed
1561
	{
unknown's avatar
unknown committed
1562 1563
	  vio_delete(con->mysql.net.vio);
	  con->mysql.net.vio = 0;
unknown's avatar
unknown committed
1564
	}
unknown's avatar
unknown committed
1565
      }
1566
#endif
1567 1568 1569 1570
      mysql_close(&con->mysql);
      DBUG_RETURN(0);
    }
  }
1571
  die("connection '%s' not found in connection pool", name);
1572
  DBUG_RETURN(1);				/* Never reached */
1573 1574
}

unknown's avatar
unknown committed
1575

1576 1577
/*
   This one now is a hack - we may want to improve in in the
unknown's avatar
unknown committed
1578 1579 1580
   future to handle quotes. For now we assume that anything that is not
   a comma, a space or ) belongs to the argument. space is a chopper, comma or
   ) are delimiters/terminators
1581
*/
1582

unknown's avatar
unknown committed
1583 1584
char* safe_get_param(char* str, char** arg, const char* msg)
{
1585
  DBUG_ENTER("safe_get_param");
1586
  while (*str && my_isspace(charset_info,*str))
unknown's avatar
unknown committed
1587
    str++;
unknown's avatar
unknown committed
1588
  *arg = str;
unknown's avatar
unknown committed
1589 1590
  for (; *str && *str != ',' && *str != ')' ; str++)
  {
1591
    if (my_isspace(charset_info,*str))
unknown's avatar
unknown committed
1592
      *str = 0;
unknown's avatar
unknown committed
1593
  }
1594
  if (!*str)
unknown's avatar
unknown committed
1595
    die(msg);
1596

unknown's avatar
unknown committed
1597
  *str++ = 0;
1598
  DBUG_RETURN(str);
unknown's avatar
unknown committed
1599 1600
}

1601
#ifndef EMBEDDED_LIBRARY
1602 1603 1604 1605 1606 1607 1608 1609 1610 1611
void init_manager()
{
  if (!(manager=mysql_manager_init(0)))
    die("Failed in mysql_manager_init()");
  if (!mysql_manager_connect(manager,manager_host,manager_user,
			     manager_pass,manager_port))
    die("Could not connect to MySQL manager: %s(%d)",manager->last_error,
	manager->last_errno);

}
1612
#endif
1613

unknown's avatar
unknown committed
1614 1615 1616 1617 1618 1619 1620 1621
int safe_connect(MYSQL* con, const char* host, const char* user,
		 const char* pass,
		 const char* db, int port, const char* sock)
{
  int con_error = 1;
  int i;
  for (i = 0; i < MAX_CON_TRIES; ++i)
  {
1622 1623
    if (mysql_real_connect(con, host,user, pass, db, port, sock,
			   CLIENT_MULTI_STATEMENTS))
unknown's avatar
unknown committed
1624 1625 1626 1627 1628 1629 1630 1631 1632
    {
      con_error = 0;
      break;
    }
    sleep(CON_RETRY_SLEEP);
  }
  return con_error;
}

1633 1634

int do_connect(struct st_query* q)
unknown's avatar
unknown committed
1635 1636 1637
{
  char* con_name, *con_user,*con_pass, *con_host, *con_port_str,
    *con_db, *con_sock;
1638
  char* p=q->first_argument;
1639
  char buff[FN_REFLEN];
unknown's avatar
unknown committed
1640
  int con_port;
1641
  int free_con_sock = 0;
unknown's avatar
unknown committed
1642

1643 1644
  DBUG_ENTER("do_connect");
  DBUG_PRINT("enter",("connect: %s",p));
unknown's avatar
unknown committed
1645

1646
  if (*p != '(')
1647
    die("Syntax error in connect - expected '(' found '%c'", *p);
unknown's avatar
unknown committed
1648 1649 1650 1651 1652 1653
  p++;
  p = safe_get_param(p, &con_name, "missing connection name");
  p = safe_get_param(p, &con_host, "missing connection host");
  p = safe_get_param(p, &con_user, "missing connection user");
  p = safe_get_param(p, &con_pass, "missing connection password");
  p = safe_get_param(p, &con_db, "missing connection db");
unknown's avatar
unknown committed
1654 1655 1656 1657 1658 1659 1660
  if (!*p || *p == ';')				/* Default port and sock */
  {
    con_port=port;
    con_sock=(char*) unix_sock;
  }
  else
  {
1661
    VAR* var_port, *var_sock;
unknown's avatar
unknown committed
1662
    p = safe_get_param(p, &con_port_str, "missing connection port");
1663 1664
    if (*con_port_str == '$')
    {
1665
      if (!(var_port = var_get(con_port_str, 0, 0, 0)))
1666 1667 1668 1669
	die("Unknown variable '%s'", con_port_str+1);
      con_port = var_port->int_val;
    }
    else
1670
      con_port=atoi(con_port_str);
unknown's avatar
unknown committed
1671
    p = safe_get_param(p, &con_sock, "missing connection socket");
1672 1673
    if (*con_sock == '$')
    {
1674
      if (!(var_sock = var_get(con_sock, 0, 0, 0)))
1675 1676 1677 1678 1679 1680 1681
	die("Unknown variable '%s'", con_sock+1);
      if (!(con_sock = (char*)my_malloc(var_sock->str_val_len+1, MYF(0))))
	die("Out of memory");
      free_con_sock = 1;
      memcpy(con_sock, var_sock->str_val, var_sock->str_val_len);
      con_sock[var_sock->str_val_len] = 0;
    }
unknown's avatar
unknown committed
1682
  }
unknown's avatar
unknown committed
1683

1684
  if (next_con == cons_end)
unknown's avatar
unknown committed
1685
    die("Connection limit exhausted - increase MAX_CONS in mysqltest.c");
unknown's avatar
unknown committed
1686

1687
  if (!mysql_init(&next_con->mysql))
unknown's avatar
unknown committed
1688
    die("Failed on mysql_init()");
unknown's avatar
unknown committed
1689 1690
  if (opt_compress)
    mysql_options(&next_con->mysql,MYSQL_OPT_COMPRESS,NullS);
1691
  mysql_options(&next_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
unknown's avatar
unknown committed
1692
  mysql_options(&next_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name);
1693

unknown's avatar
unknown committed
1694 1695 1696 1697 1698
#ifdef HAVE_OPENSSL
  if (opt_use_ssl)
    mysql_ssl_set(&next_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
		  opt_ssl_capath, opt_ssl_cipher);
#endif
1699
  if (con_sock && !free_con_sock && *con_sock && *con_sock != FN_LIBCHAR)
1700
    con_sock=fn_format(buff, con_sock, TMPDIR, "",0);
unknown's avatar
unknown committed
1701 1702
  if (!con_db[0])
    con_db=db;
1703
  /* Special database to allow one to connect without a database name */
unknown's avatar
unknown committed
1704
  if (con_db && !strcmp(con_db,"*NO-ONE*"))
1705
    con_db=0;
unknown's avatar
unknown committed
1706 1707 1708
  if ((safe_connect(&next_con->mysql, con_host,
		    con_user, con_pass,
		    con_db, con_port, con_sock ? con_sock: 0)))
unknown's avatar
unknown committed
1709 1710 1711
    die("Could not open connection '%s': %s", con_name,
	mysql_error(&next_con->mysql));

1712
  if (!(next_con->name = my_strdup(con_name, MYF(MY_WME))))
1713
    die(NullS);
unknown's avatar
unknown committed
1714
  cur_con = next_con++;
1715 1716
  if (free_con_sock)
    my_free(con_sock, MYF(MY_WME));
1717
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1718 1719
}

1720

1721
int do_done(struct st_query* q)
1722 1723
{
  q->type = Q_END_BLOCK;
1724
  if (cur_block == block_stack)
1725
    die("Stray '}' - end of block before beginning");
1726 1727
  if (*block_ok--)
  {
1728
    parser.current_line = *--cur_block;
1729
  }
1730
  else
unknown's avatar
unknown committed
1731
  {
1732 1733
    ++parser.current_line;
    --cur_block;
unknown's avatar
unknown committed
1734
  }
1735 1736 1737
  return 0;
}

1738
int do_while(struct st_query* q)
1739
{
1740
  char* p=q->first_argument;
1741
  const char* expr_start, *expr_end;
1742
  VAR v;
1743
  if (cur_block == block_stack_end)
1744
    die("Nesting too deeply");
1745
  if (!*block_ok)
1746 1747 1748 1749 1750 1751
  {
    ++false_block_depth;
    *++block_ok = 0;
    *cur_block++ = parser.current_line++;
    return 0;
  }
unknown's avatar
unknown committed
1752

1753
  expr_start = strchr(p, '(');
1754
  if (!expr_start)
1755 1756
    die("missing '(' in while");
  expr_end = strrchr(expr_start, ')');
1757
  if (!expr_end)
1758
    die("missing ')' in while");
unknown's avatar
unknown committed
1759
  var_init(&v,0,0,0,0);
1760
  eval_expr(&v, ++expr_start, &expr_end);
1761
  *cur_block++ = parser.current_line++;
1762
  if (!v.int_val)
1763 1764 1765 1766
  {
    *++block_ok = 0;
    false_block_depth++;
  }
1767 1768
  else
    *++block_ok = 1;
unknown's avatar
unknown committed
1769
  var_free(&v);
1770
  return 0;
1771 1772
}

unknown's avatar
unknown committed
1773

1774 1775 1776 1777 1778 1779 1780 1781 1782 1783
/*
  Read characters from line buffer or file. This is needed to allow
  my_ungetc() to buffer MAX_DELIMITER characters for a file

  NOTE:
    This works as long as one doesn't change files (with 'source file_name')
    when there is things pushed into the buffer.  This should however not
    happen for any tests in the test suite.
*/

1784
int my_getc(FILE *file)
1785 1786 1787
{
  if (line_buffer_pos == line_buffer)
    return fgetc(file);
1788
  return *--line_buffer_pos;
1789 1790 1791 1792
}

void my_ungetc(int c)
{
1793
  *line_buffer_pos++= (char) c;
1794 1795 1796 1797
}


my_bool end_of_query(int c)
1798
{
1799
  uint i;
1800
  char tmp[MAX_DELIMITER];
1801

1802 1803 1804 1805 1806 1807
  if (c != *delimiter)
    return 0;

  for (i= 1; i < delimiter_length &&
	 (c= my_getc(*cur_file)) == *(delimiter + i);
       i++)
1808 1809 1810
    tmp[i]= c;

  if (i == delimiter_length)
1811 1812 1813
    return 1;					/* Found delimiter */

  /* didn't find delimiter, push back things that we read */
1814 1815 1816
  my_ungetc(c);
  while (i > 1)
    my_ungetc(tmp[--i]);
1817 1818 1819 1820
  return 0;
}


unknown's avatar
unknown committed
1821 1822 1823
int read_line(char* buf, int size)
{
  int c;
1824 1825
  char* p= buf, *buf_end= buf + size - 1;
  int no_save= 0;
unknown's avatar
unknown committed
1826 1827
  enum {R_NORMAL, R_Q1, R_ESC_Q_Q1, R_ESC_Q_Q2,
	R_ESC_SLASH_Q1, R_ESC_SLASH_Q2,
1828
	R_Q2, R_COMMENT, R_LINE_START} state= R_LINE_START;
unknown's avatar
unknown committed
1829
  DBUG_ENTER("read_line");
1830

1831
  start_lineno= *lineno;
1832 1833
  for (; p < buf_end ;)
  {
1834
    no_save= 0;
1835
    c= my_getc(*cur_file);
1836
    if (feof(*cur_file))
unknown's avatar
unknown committed
1837
    {
unknown's avatar
unknown committed
1838
  found_eof:
1839
      if ((*cur_file) != stdin)
1840
	my_fclose(*cur_file, MYF(0));
unknown's avatar
unknown committed
1841 1842
      cur_file--;
      lineno--;
1843
      if (cur_file == file_stack)
unknown's avatar
unknown committed
1844
	DBUG_RETURN(1);
unknown's avatar
unknown committed
1845
      continue;
1846
    }
1847

1848 1849 1850 1851
    /* Line counting is independent of state */
    if (c == '\n')
      (*lineno)++;

1852 1853
    switch(state) {
    case R_NORMAL:
1854
      /*  Only accept '{' in the beginning of a line */
1855 1856 1857
      if (end_of_query(c))
      {
	*p= 0;
unknown's avatar
unknown committed
1858
	DBUG_RETURN(0);
1859
      }
1860 1861 1862 1863 1864
      else if (c == '\'')
	state = R_Q1;
      else if (c == '"')
	state = R_Q2;
      else if (c == '\n')
1865
      {
1866
	state = R_LINE_START;
1867
      }
1868 1869 1870 1871
      break;
    case R_COMMENT:
      if (c == '\n')
      {
1872
	*p= 0;
unknown's avatar
unknown committed
1873
	DBUG_RETURN(0);
1874 1875 1876 1877 1878 1879 1880
      }
      break;
    case R_LINE_START:
      if (c == '#' || c == '-')
      {
	state = R_COMMENT;
      }
1881
      else if (my_isspace(charset_info, c))
1882 1883
      {
	if (c == '\n')
1884
	  start_lineno= *lineno;		/* Query hasn't started yet */
1885
	no_save= 1;
1886
      }
1887 1888
      else if (c == '}')
      {
1889 1890
	*buf++= '}';
	*buf= 0;
unknown's avatar
unknown committed
1891
	DBUG_RETURN(0);
1892
      }
1893
      else if (end_of_query(c) || c == '{')
1894
      {
1895
	*p= 0;
unknown's avatar
unknown committed
1896
	DBUG_RETURN(0);
1897
      }
unknown's avatar
unknown committed
1898
      else if (c == '\'')
1899
	state= R_Q1;
unknown's avatar
unknown committed
1900
      else if (c == '"')
1901
	state= R_Q2;
1902
      else
1903
	state= R_NORMAL;
1904
      break;
1905

1906 1907
    case R_Q1:
      if (c == '\'')
1908
	state= R_ESC_Q_Q1;
1909
      else if (c == '\\')
1910
	state= R_ESC_SLASH_Q1;
1911 1912
      break;
    case R_ESC_Q_Q1:
1913 1914 1915
      if (end_of_query(c))
      {
	*p= 0;
unknown's avatar
unknown committed
1916
	DBUG_RETURN(0);
1917
      }
1918
      if (c != '\'')
1919
	state= R_NORMAL;
1920
      else
1921
	state= R_Q1;
1922 1923
      break;
    case R_ESC_SLASH_Q1:
1924
      state= R_Q1;
1925
      break;
unknown's avatar
unknown committed
1926

1927 1928
    case R_Q2:
      if (c == '"')
1929
	state= R_ESC_Q_Q2;
1930
      else if (c == '\\')
1931
	state= R_ESC_SLASH_Q2;
1932 1933
      break;
    case R_ESC_Q_Q2:
1934 1935 1936
      if (end_of_query(c))
      {
	*p= 0;
unknown's avatar
unknown committed
1937
	DBUG_RETURN(0);
1938
      }
1939
      if (c != '"')
1940
	state= R_NORMAL;
1941
      else
1942
	state= R_Q2;
1943 1944
      break;
    case R_ESC_SLASH_Q2:
1945
      state= R_Q2;
1946
      break;
unknown's avatar
unknown committed
1947
    }
1948 1949

    if (!no_save)
unknown's avatar
unknown committed
1950 1951 1952 1953 1954
    {
      /* Could be a multibyte character */
      /* This code is based on the code in "sql_load.cc" */
#ifdef USE_MB
      int charlen = my_mbcharlen(charset_info, c);
unknown's avatar
Merge  
unknown committed
1955 1956
      /* We give up if multibyte character is started but not */
      /* completed before we pass buf_end */
unknown's avatar
unknown committed
1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982
      if ((charlen > 1) && (p + charlen) <= buf_end)
      {
	int i;
	char* mb_start = p;

	*p++ = c;

	for (i= 1; i < charlen; i++)
	{
	  if (feof(*cur_file))
	    goto found_eof;	/* FIXME: could we just break here?! */
	  c= my_getc(*cur_file);
	  *p++ = c;
	}
	if (! my_ismbchar(charset_info, mb_start, p))
	{
	  /* It was not a multiline char, push back the characters */
	  /* We leave first 'c', i.e. pretend it was a normal char */
	  while (p > mb_start)
	    my_ungetc(*--p);
	}
      }
      else
#endif
	*p++= c;
    }
1983
  }
1984
  *p= 0;					/* Always end with \0 */
unknown's avatar
unknown committed
1985
  DBUG_RETURN(feof(*cur_file));
unknown's avatar
unknown committed
1986 1987
}

unknown's avatar
unknown committed
1988

1989 1990
static char read_query_buf[MAX_QUERY];

1991
int read_query(struct st_query** q_ptr)
unknown's avatar
unknown committed
1992
{
1993 1994
  char *p = read_query_buf, * p1 ;
  struct st_query* q;
unknown's avatar
unknown committed
1995
  DBUG_ENTER("read_query");
1996

1997 1998
  if (parser.current_line < parser.read_lines)
  {
1999
    get_dynamic(&q_lines, (gptr) q_ptr, parser.current_line) ;
unknown's avatar
unknown committed
2000
    DBUG_RETURN(0);
2001
  }
2002
  if (!(*q_ptr= q= (struct st_query*) my_malloc(sizeof(*q), MYF(MY_WME))) ||
unknown's avatar
unknown committed
2003 2004
      insert_dynamic(&q_lines, (gptr) &q))
    die(NullS);
2005

2006 2007 2008
  q->record_file[0]= 0;
  q->require_file= 0;
  q->first_word_len= 0;
unknown's avatar
unknown committed
2009

2010
  q->type = Q_UNKNOWN;
2011
  q->query_buf= q->query= 0;
2012
  if (read_line(read_query_buf, sizeof(read_query_buf)))
unknown's avatar
unknown committed
2013 2014
  {
    DBUG_PRINT("warning",("too long query"));
unknown's avatar
unknown committed
2015
    DBUG_RETURN(1);
unknown's avatar
unknown committed
2016 2017
  }
   DBUG_PRINT("info", ("query: %s", read_query_buf));
2018 2019 2020
  if (*p == '#')
  {
    q->type = Q_COMMENT;
unknown's avatar
unknown committed
2021 2022
    /* This goto is to avoid losing the "expected error" info. */
    goto end;
2023
  }
unknown's avatar
unknown committed
2024 2025
  memcpy((gptr) q->expected_errno, (gptr) global_expected_errno,
	 sizeof(global_expected_errno));
unknown's avatar
unknown committed
2026
  q->expected_errors= global_expected_errors;
2027
  q->abort_on_error= (global_expected_errors == 0 && abort_on_error);
unknown's avatar
unknown committed
2028
  bzero((gptr) global_expected_errno, sizeof(global_expected_errno));
unknown's avatar
unknown committed
2029 2030
  global_expected_errors=0;
  if (p[0] == '-' && p[1] == '-')
2031
  {
2032 2033
    q->type= Q_COMMENT_WITH_COMMAND;
    p+= 2;					/* To calculate first word */
2034 2035 2036 2037
  }
  else
  {
    if (*p == '!')
unknown's avatar
unknown committed
2038
    {
2039
      q->abort_on_error= 0;
2040 2041 2042
      p++;
      if (*p == '$')
      {
2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061
        int expected_errno= 0;
        p++;
        for (; my_isdigit(charset_info, *p); p++)
          expected_errno = expected_errno * 10 + *p - '0';
        q->expected_errno[0].code.errnum = expected_errno;
        q->expected_errno[0].type= ERR_ERRNO;
        q->expected_errno[1].type= ERR_EMPTY;
        q->expected_errors=1;
      }
      else if (*p == 'S')                            /* SQLSTATE */
      {
        int i;
        p++;
        for (i = 0; my_isalnum(charset_info, *p) && i < SQLSTATE_LENGTH; p++, i++)
          q->expected_errno[0].code.sqlstate[i]= *p;
        q->expected_errno[0].code.sqlstate[i]= '\0';
        q->expected_errno[0].type= ERR_SQLSTATE;
        q->expected_errno[1].type= ERR_EMPTY;
        q->expected_errors=1;
2062
      }
unknown's avatar
unknown committed
2063 2064
    }

2065
    while (*p && my_isspace(charset_info, *p))
unknown's avatar
unknown committed
2066
      p++ ;
2067
    if (*p == '@')
unknown's avatar
unknown committed
2068 2069 2070
    {
      p++;
      p1 = q->record_file;
2071
      while (!my_isspace(charset_info, *p) &&
unknown's avatar
unknown committed
2072
	     p1 < q->record_file + sizeof(q->record_file) - 1)
unknown's avatar
unknown committed
2073
	*p1++ = *p++;
2074
      *p1 = 0;
unknown's avatar
unknown committed
2075
    }
2076
  }
unknown's avatar
unknown committed
2077 2078

end:
2079
  while (*p && my_isspace(charset_info, *p))
unknown's avatar
unknown committed
2080
    p++;
2081
  if (!(q->query_buf= q->query= my_strdup(p, MYF(MY_WME))))
2082 2083 2084
    die(NullS);

  /* Calculate first word and first argument */
2085 2086 2087
  for (p= q->query; *p && !my_isspace(charset_info, *p) ; p++) ;
  q->first_word_len= (uint) (p - q->query);
  while (*p && my_isspace(charset_info, *p))
unknown's avatar
unknown committed
2088
    p++;
2089 2090
  q->first_argument= p;
  q->end= strend(q->query);
2091
  parser.read_lines++;
unknown's avatar
unknown committed
2092
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2093 2094
}

2095 2096 2097

static struct my_option my_long_options[] =
{
2098
  {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
2099 2100 2101
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
  {"database", 'D', "Database to use.", (gptr*) &db, (gptr*) &db, 0,
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2102
  {"basedir", 'b', "Basedir for tests.", (gptr*) &opt_basedir,
2103
   (gptr*) &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2104
  {"big-test", 'B', "Define BIG_TEST to 1.", (gptr*) &opt_big_test,
2105
   (gptr*) &opt_big_test, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
2106
  {"compress", 'C', "Use the compressed server/client protocol.",
2107 2108 2109 2110 2111 2112
   (gptr*) &opt_compress, (gptr*) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
   0, 0, 0},
  {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
   0, 0, 0, 0, 0, 0},
  {"host", 'h', "Connect to host.", (gptr*) &host, (gptr*) &host, 0,
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2113
  {"manager-user", OPT_MANAGER_USER, "Undocumented: Used for debugging.",
2114 2115
   (gptr*) &manager_user, (gptr*) &manager_user, 0, GET_STR, REQUIRED_ARG, 0,
   0, 0, 0, 0, 0},
2116
  {"manager-host", OPT_MANAGER_HOST, "Undocumented: Used for debugging.",
2117 2118
   (gptr*) &manager_host, (gptr*) &manager_host, 0, GET_STR, REQUIRED_ARG,
   0, 0, 0, 0, 0, 0},
2119
  {"manager-password", OPT_MANAGER_PASSWD, "Undocumented: Used for debugging.",
2120
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2121
  {"manager-port", OPT_MANAGER_PORT, "Undocumented: Used for debugging.",
2122 2123 2124
   (gptr*) &manager_port, (gptr*) &manager_port, 0, GET_INT, REQUIRED_ARG,
   MYSQL_MANAGER_PORT, 0, 0, 0, 0, 0},
  {"manager-wait-timeout", OPT_MANAGER_WAIT_TIMEOUT,
2125
   "Undocumented: Used for debugging.", (gptr*) &manager_wait_timeout,
2126 2127 2128 2129
   (gptr*) &manager_wait_timeout, 0, GET_INT, REQUIRED_ARG, 3, 0, 0, 0, 0, 0},
  {"password", 'p', "Password to use when connecting to server.",
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
  {"port", 'P', "Port number to use for connection.", (gptr*) &port,
unknown's avatar
unknown committed
2130
   (gptr*) &port, 0, GET_INT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0, 0},
2131 2132 2133
  {"ps-protocol", OPT_PS_PROTOCOL, "Use prepared statements protocol for communication",
   (gptr*) &ps_protocol, (gptr*) &ps_protocol, 0,
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
2134 2135 2136 2137 2138 2139 2140
  {"quiet", 's', "Suppress all normal output.", (gptr*) &silent,
   (gptr*) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"record", 'r', "Record output of test_file into result file.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"result-file", 'R', "Read/Store result from/in this file.",
   (gptr*) &result_file, (gptr*) &result_file, 0, GET_STR, REQUIRED_ARG,
   0, 0, 0, 0, 0, 0},
2141
  {"server-arg", 'A', "Send enbedded server this as a paramenter.",
2142
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2143
  {"server-file", 'F', "Read embedded server arguments from file.",
2144 2145 2146
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"silent", 's', "Suppress all normal output. Synonym for --quiet.",
   (gptr*) &silent, (gptr*) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
2147
  {"skip-safemalloc", OPT_SKIP_SAFEMALLOC,
2148
   "Don't use the memory allocation checking.", 0, 0, 0, GET_NO_ARG, NO_ARG,
2149
   0, 0, 0, 0, 0, 0},
2150
  {"sleep", 'T', "Sleep always this many seconds on sleep commands.",
2151 2152 2153 2154 2155
   (gptr*) &opt_sleep, (gptr*) &opt_sleep, 0, GET_INT, REQUIRED_ARG, 0, 0, 0,
   0, 0, 0},
  {"socket", 'S', "Socket file to use for connection.",
   (gptr*) &unix_sock, (gptr*) &unix_sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0,
   0, 0, 0},
unknown's avatar
unknown committed
2156
#include "sslopt-longopts.h"
2157 2158
  {"test-file", 'x', "Read test from/in this file (default stdin).",
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
2159 2160
  {"timer-file", 'm', "File where the timing in micro seconds is stored.",
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2161
  {"tmpdir", 't', "Temporary directory where sockets are put.",
2162 2163 2164 2165 2166 2167 2168 2169
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"user", 'u', "User for login.", (gptr*) &user, (gptr*) &user, 0, GET_STR,
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"verbose", 'v', "Write more.", (gptr*) &verbose, (gptr*) &verbose, 0,
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"version", 'V', "Output version information and exit.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
unknown's avatar
unknown committed
2170 2171
};

unknown's avatar
unknown committed
2172 2173 2174

#include <help_start.h>

unknown's avatar
unknown committed
2175 2176 2177 2178 2179 2180 2181 2182 2183
static void print_version(void)
{
  printf("%s  Ver %s Distrib %s, for %s (%s)\n",my_progname,MTEST_VERSION,
	 MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE);
}

void usage()
{
  print_version();
2184
  printf("MySQL AB, by Sasha, Matt, Monty & Jani\n");
unknown's avatar
unknown committed
2185 2186 2187
  printf("This software comes with ABSOLUTELY NO WARRANTY\n\n");
  printf("Runs a test against the mysql server and compares output with a results file.\n\n");
  printf("Usage: %s [OPTIONS] [database] < test_file\n", my_progname);
2188
  my_print_help(my_long_options);
unknown's avatar
unknown committed
2189
  printf("  --no-defaults       Don't read default options from any options file.\n");
2190
  my_print_variables(my_long_options);
unknown's avatar
unknown committed
2191 2192
}

unknown's avatar
unknown committed
2193 2194
#include <help_end.h>

2195 2196 2197 2198 2199

static my_bool
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
	       char *argument)
{
2200
  switch(optid) {
2201
  case '#':
unknown's avatar
unknown committed
2202
#ifndef DBUG_OFF
2203
    DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysqltest.trace");
unknown's avatar
unknown committed
2204
#endif
2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222
    break;
  case 'r':
    record = 1;
    break;
  case (int)OPT_MANAGER_PASSWD:
    my_free(manager_pass,MYF(MY_ALLOW_ZERO_PTR));
    manager_pass=my_strdup(argument, MYF(MY_FAE));
    while (*argument) *argument++= 'x';		/* Destroy argument */
    break;
  case 'x':
    {
      char buff[FN_REFLEN];
      if (!test_if_hard_path(argument))
      {
	strxmov(buff, opt_basedir, argument, NullS);
	argument= buff;
      }
      fn_format(buff, argument, "", "", 4);
unknown's avatar
unknown committed
2223
      if (!(*++cur_file = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
2224 2225 2226
	die("Could not open %s: errno = %d", argument, errno);
      break;
    }
unknown's avatar
unknown committed
2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239
  case 'm':
    {
      static char buff[FN_REFLEN];
      if (!test_if_hard_path(argument))
      {
	strxmov(buff, opt_basedir, argument, NullS);
	argument= buff;
      }
      fn_format(buff, argument, "", "", 4);
      timer_file= buff;
      unlink(timer_file);	     /* Ignore error, may not exist */
      break;
    }
2240 2241 2242 2243 2244 2245
  case 'p':
    if (argument)
    {
      my_free(pass, MYF(MY_ALLOW_ZERO_PTR));
      pass= my_strdup(argument, MYF(MY_FAE));
      while (*argument) *argument++= 'x';		/* Destroy argument */
2246
      tty_password= 0;
2247 2248 2249 2250
    }
    else
      tty_password= 1;
    break;
unknown's avatar
unknown committed
2251
#include <sslopt-case.h>
2252 2253 2254 2255 2256 2257 2258 2259 2260
  case 't':
    strnmov(TMPDIR, argument, sizeof(TMPDIR));
    break;
  case 'A':
    if (!embedded_server_arg_count)
    {
      embedded_server_arg_count=1;
      embedded_server_args[0]= (char*) "";
    }
2261 2262 2263
    if (embedded_server_arg_count == MAX_SERVER_ARGS-1 ||
        !(embedded_server_args[embedded_server_arg_count++]=
          my_strdup(argument, MYF(MY_FAE))))
2264 2265 2266 2267 2268 2269 2270 2271
    {
      die("Can't use server argument");
    }
    break;
  case 'F':
    if (read_server_arguments(argument))
      die(NullS);
    break;
2272 2273 2274 2275 2276
  case OPT_SKIP_SAFEMALLOC:
#ifdef SAFEMALLOC
    sf_malloc_quick=1;
#endif
    break;
2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287
  case 'V':
    print_version();
    exit(0);
  case '?':
    usage();
    exit(1);
  }
  return 0;
}


unknown's avatar
unknown committed
2288 2289 2290
int parse_args(int argc, char **argv)
{
  load_defaults("my",load_default_groups,&argc,&argv);
2291
  default_argv= argv;
2292

2293
  if ((handle_options(&argc, &argv, my_long_options, get_one_option)))
unknown's avatar
unknown committed
2294
    exit(1);
unknown's avatar
unknown committed
2295 2296 2297 2298 2299 2300 2301

  if (argc > 1)
  {
    usage();
    exit(1);
  }
  if (argc == 1)
2302
    db= *argv;
unknown's avatar
unknown committed
2303 2304 2305 2306 2307 2308
  if (tty_password)
    pass=get_tty_password(NullS);

  return 0;
}

unknown's avatar
unknown committed
2309
char* safe_str_append(char* buf, const char* str, int size)
unknown's avatar
unknown committed
2310 2311
{
  int i,c ;
2312
  for (i = 0; (c = *str++) &&  i < size - 1; i++)
unknown's avatar
unknown committed
2313 2314 2315 2316 2317
    *buf++ = c;
  *buf = 0;
  return buf;
}

unknown's avatar
unknown committed
2318 2319 2320
void str_to_file(const char* fname, char* str, int size)
{
  int fd;
2321
  char buff[FN_REFLEN];
2322 2323 2324 2325 2326 2327
  if (!test_if_hard_path(fname))
  {
    strxmov(buff, opt_basedir, fname, NullS);
    fname=buff;
  }
  fn_format(buff,fname,"","",4);
unknown's avatar
unknown committed
2328

2329
  if ((fd = my_open(buff, O_WRONLY | O_CREAT | O_TRUNC,
2330
		    MYF(MY_WME | MY_FFNF))) < 0)
2331
    die("Could not open %s: errno = %d", buff, errno);
2332
  if (my_write(fd, (byte*)str, size, MYF(MY_WME|MY_FNABP)))
unknown's avatar
unknown committed
2333 2334 2335 2336 2337
    die("write failed");
  my_close(fd, MYF(0));
}

void reject_dump(const char* record_file, char* buf, int size)
unknown's avatar
unknown committed
2338
{
unknown's avatar
unknown committed
2339
  char reject_file[FN_REFLEN];
unknown's avatar
unknown committed
2340
  str_to_file(fn_format(reject_file, record_file,"",".reject",2), buf, size);
unknown's avatar
unknown committed
2341 2342
}

2343 2344 2345

/* Append the string to ds, with optional replace */

unknown's avatar
unknown committed
2346 2347
static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
				      int len)
2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358
{
  if (glob_replace)
  {
    len=(int) replace_strings(glob_replace, &out_buff, &out_length, val);
    if (len == -1)
      die("Out of memory in replace\n");
    val=out_buff;
  }
  dynstr_append_mem(ds, val, len);
}

2359

2360 2361
/*
  Append all results to the dynamic string separated with '\t'
2362
  Values may be converted with 'replace_column'
2363 2364 2365 2366 2367
*/

static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
{
  MYSQL_ROW row;
2368
  uint num_fields= mysql_num_fields(res);
2369
  MYSQL_FIELD *fields= !display_result_vertically ? 0 : mysql_fetch_fields(res);
2370 2371 2372
  unsigned long *lengths;
  while ((row = mysql_fetch_row(res)))
  {
2373
    uint i;
2374 2375 2376 2377 2378
    lengths = mysql_fetch_lengths(res);
    for (i = 0; i < num_fields; i++)
    {
      const char *val= row[i];
      ulonglong len= lengths[i];
2379 2380 2381 2382 2383 2384

      if (i < max_replace_column && replace_column[i])
      {
	val= replace_column[i];
	len= strlen(val);
      }
2385 2386
      if (!val)
      {
2387 2388
	val= "NULL";
	len= 4;
2389
      }
2390 2391 2392 2393 2394 2395 2396 2397 2398
      if (!display_result_vertically)
      {
	if (i)
	  dynstr_append_mem(ds, "\t", 1);
	replace_dynstr_append_mem(ds, val, len);
      }
      else
      {
	dynstr_append(ds, fields[i].name);
2399
	dynstr_append_mem(ds, "\t", 1);
2400 2401 2402
	replace_dynstr_append_mem(ds, val, len);
	dynstr_append_mem(ds, "\n", 1);
      }
2403
    }
2404 2405
    if (!display_result_vertically)
      dynstr_append_mem(ds, "\n", 1);
2406
  }
2407
  free_replace_column();
2408 2409
}

2410

2411
/*
2412
* flags control the phased/stages of query execution to be performed
2413 2414 2415
* if QUERY_SEND bit is on, the query will be sent. If QUERY_REAP is on
* the result will be read - for regular query, both bits must be on
*/
2416

2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440
static int run_query_normal(MYSQL *mysql, struct st_query *q, int flags);
static int run_query_stmt  (MYSQL *mysql, struct st_query *q, int flags);
static void run_query_stmt_handle_warnings(MYSQL *mysql, DYNAMIC_STRING *ds);
static int run_query_stmt_handle_error(char *query, struct st_query *q,
                                       MYSQL_STMT *stmt, DYNAMIC_STRING *ds);
static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields,
                                       DYNAMIC_STRING *ds);

static int run_query(MYSQL *mysql, struct st_query *q, int flags)
{

  /*
    Try to find out if we can run this statement using the prepared
    statement protocol.

    We don't have a mysql_stmt_send_execute() so we only handle
    complete SEND+REAP.

    If it is a '?' in the query it may be a SQL level prepared
     statement already and we can't do it twice
  */

  if (ps_protocol_enabled && disable_info &&
      (flags & QUERY_SEND) && (flags & QUERY_REAP) && ps_match_re(q->query))
2441
    return run_query_stmt(mysql, q, flags);
2442 2443 2444 2445 2446
  return run_query_normal(mysql, q, flags);
}


static int run_query_normal(MYSQL* mysql, struct st_query* q, int flags)
unknown's avatar
unknown committed
2447
{
2448
  MYSQL_RES* res= 0;
2449 2450
  uint i;
  int error= 0, err= 0, counter= 0;
2451 2452
  DYNAMIC_STRING *ds;
  DYNAMIC_STRING ds_tmp;
2453
  DYNAMIC_STRING eval_query;
2454
  char* query;
2455
  int query_len, got_error_on_send= 0;
2456
  DBUG_ENTER("run_query_normal");
unknown's avatar
unknown committed
2457
  DBUG_PRINT("enter",("flags: %d", flags));
unknown's avatar
unknown committed
2458
  
unknown's avatar
unknown committed
2459 2460 2461 2462 2463
  if (q->type != Q_EVAL)
  {
    query = q->query;
    query_len = strlen(query);
  }
2464
  else
unknown's avatar
unknown committed
2465 2466 2467 2468 2469 2470
  {
    init_dynamic_string(&eval_query, "", 16384, 65536);
    do_eval(&eval_query, q->query);
    query = eval_query.str;
    query_len = eval_query.length;
  }
unknown's avatar
unknown committed
2471
  DBUG_PRINT("enter", ("query: '%-.60s'", query));
unknown's avatar
unknown committed
2472

unknown's avatar
unknown committed
2473
  if (q->record_file[0])
2474
  {
2475
    init_dynamic_string(&ds_tmp, "", 16384, 65536);
2476 2477
    ds = &ds_tmp;
  }
2478 2479
  else
    ds= &ds_res;
unknown's avatar
unknown committed
2480

2481 2482 2483
  if (flags & QUERY_SEND)
  {
    got_error_on_send= mysql_send_query(mysql, query, query_len);
2484
    if (got_error_on_send && q->expected_errno[0].type == ERR_EMPTY)
2485 2486 2487
      die("At line %u: unable to send query '%s' (mysql_errno=%d , errno=%d)",
	  start_lineno, query, mysql_errno(mysql), errno);
  }
unknown's avatar
unknown committed
2488

2489
  do
2490
  {
2491
    if ((flags & QUERY_SEND) && !disable_query_log && !counter)
2492
    {
2493
      replace_dynstr_append_mem(ds,query, query_len);
2494 2495
      dynstr_append_mem(ds, delimiter, delimiter_length);
      dynstr_append_mem(ds, "\n", 1);
2496
    }
2497 2498 2499
    if (!(flags & QUERY_REAP))
      DBUG_RETURN(0);

2500 2501 2502 2503
    if (got_error_on_send ||
	(!counter && (*mysql->methods->read_query_result)(mysql)) ||
	 (!(last_result= res= mysql_store_result(mysql)) &&
	  mysql_field_count(mysql)))
unknown's avatar
unknown committed
2504
    {
2505
      if (q->require_file)
2506
      {
2507 2508 2509 2510 2511 2512 2513 2514
	abort_not_supported_test();
      }
      if (q->abort_on_error)
	die("At line %u: query '%s' failed: %d: %s", start_lineno, query,
	    mysql_errno(mysql), mysql_error(mysql));
      else
      {
	for (i=0 ; (uint) i < q->expected_errors ; i++)
unknown's avatar
unknown committed
2515
	{
2516 2517 2518 2519
          if (((q->expected_errno[i].type == ERR_ERRNO) &&
               (q->expected_errno[i].code.errnum == mysql_errno(mysql))) ||
              ((q->expected_errno[i].type == ERR_SQLSTATE) &&
               (strcmp(q->expected_errno[i].code.sqlstate,mysql_sqlstate(mysql)) == 0)))
unknown's avatar
unknown committed
2520
	  {
2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532
	    if (i == 0 && q->expected_errors == 1)
	    {
	      /* Only log error if there is one possible error */
	      dynstr_append_mem(ds,"ERROR ",6);
	      replace_dynstr_append_mem(ds, mysql_sqlstate(mysql),
					strlen(mysql_sqlstate(mysql)));
	      dynstr_append_mem(ds, ": ", 2);
	      replace_dynstr_append_mem(ds,mysql_error(mysql),
					strlen(mysql_error(mysql)));
	      dynstr_append_mem(ds,"\n",1);
	    }
	    /* Don't log error if we may not get an error */
2533 2534 2535
            else if (q->expected_errno[0].type == ERR_SQLSTATE ||
                     (q->expected_errno[0].type == ERR_ERRNO &&
                      q->expected_errno[0].code.errnum != 0))
2536 2537
	      dynstr_append(ds,"Got one of the listed errors\n");
	    goto end;				/* Ok */
unknown's avatar
unknown committed
2538
	  }
unknown's avatar
unknown committed
2539
	}
unknown's avatar
unknown committed
2540 2541
	DBUG_PRINT("info",("i: %d  expected_errors: %d", i,
			   q->expected_errors));
2542 2543 2544 2545 2546 2547 2548 2549 2550
	dynstr_append_mem(ds, "ERROR ",6);
	replace_dynstr_append_mem(ds, mysql_sqlstate(mysql),
				  strlen(mysql_sqlstate(mysql)));
	dynstr_append_mem(ds,": ",2);
	replace_dynstr_append_mem(ds, mysql_error(mysql),
				  strlen(mysql_error(mysql)));
	dynstr_append_mem(ds,"\n",1);
	if (i)
	{
2551 2552 2553 2554 2555 2556
          if (q->expected_errno[0].type == ERR_ERRNO)
            verbose_msg("query '%s' failed with wrong errno %d instead of %d...",
                        q->query, mysql_errno(mysql), q->expected_errno[0].code.errnum);
          else
            verbose_msg("query '%s' failed with wrong sqlstate %s instead of %s...",
                        q->query, mysql_sqlstate(mysql), q->expected_errno[0].code.sqlstate);
2557 2558 2559 2560 2561 2562 2563 2564 2565
	  error= 1;
	  goto end;
	}
	verbose_msg("query '%s' failed: %d: %s", q->query, mysql_errno(mysql),
		    mysql_error(mysql));
	/*
	  if we do not abort on error, failure to run the query does
	  not fail the whole test case
	*/
2566 2567
	goto end;
      }
2568 2569 2570 2571 2572 2573
      /*{
	verbose_msg("failed in mysql_store_result for query '%s' (%d)", query,
	mysql_errno(mysql));
	error = 1;
	goto end;
	}*/
unknown's avatar
unknown committed
2574
    }
2575

2576 2577
    if (q->expected_errno[0].type == ERR_ERRNO &&
        q->expected_errno[0].code.errnum != 0)
2578
    {
2579
      /* Error code we wanted was != 0, i.e. not an expected success */
2580
      verbose_msg("query '%s' succeeded - should have failed with errno %d...",
2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591
                  q->query, q->expected_errno[0].code.errnum);
      error = 1;
      goto end;
    }
    else if (q->expected_errno[0].type == ERR_SQLSTATE &&
             strcmp(q->expected_errno[0].code.sqlstate,"00000") != 0)
    {
      /* SQLSTATE we wanted was != "00000", i.e. not an expected success */
      verbose_msg("query '%s' succeeded - should have failed with sqlstate %s...",
                  q->query, q->expected_errno[0].code.sqlstate);
      error = 1;
unknown's avatar
unknown committed
2592
      goto end;
2593
    }
2594

2595
    if (!disable_result_log)
2596
    {
2597
      if (res)
2598
      {
2599
	MYSQL_FIELD *field= mysql_fetch_fields(res);
2600
	uint num_fields= mysql_num_fields(res);
2601

2602
	if (display_metadata)
2603 2604
          run_query_display_metadata(field, num_fields, ds);

2605
	if (!display_result_vertically)
2606
	{
2607 2608 2609 2610
	  for (i = 0; i < num_fields; i++)
	  {
	    if (i)
	      dynstr_append_mem(ds, "\t", 1);
2611 2612
	    replace_dynstr_append_mem(ds, field[i].name,
				      strlen(field[i].name));
2613 2614
	  }
	  dynstr_append_mem(ds, "\n", 1);
2615 2616
	}
	append_result(ds, res);
2617
      }
unknown's avatar
unknown committed
2618

2619 2620
      /* Add all warnings to the result */
      if (!disable_warnings && mysql_warning_count(mysql))
2621
      {
2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636
	MYSQL_RES *warn_res=0;
	uint count= mysql_warning_count(mysql);
	if (!mysql_real_query(mysql, "SHOW WARNINGS", 13))
	{
	  warn_res= mysql_store_result(mysql);
	}
	if (!warn_res)
	  verbose_msg("Warning count is %u but didn't get any warnings\n",
		      count);
	else
	{
	  dynstr_append_mem(ds, "Warnings:\n", 10);
	  append_result(ds, warn_res);
	  mysql_free_result(warn_res);
	}
2637
      }
unknown's avatar
unknown committed
2638
      if (!disable_info)
2639
      {
unknown's avatar
unknown committed
2640
	char buf[40];
2641
	sprintf(buf,"affected rows: %lu\n",(ulong) mysql_affected_rows(mysql));
unknown's avatar
unknown committed
2642 2643 2644 2645 2646 2647 2648
	dynstr_append(ds, buf);
	if (mysql_info(mysql))
	{
	  dynstr_append(ds, "info: ");
	  dynstr_append(ds, mysql_info(mysql));
	  dynstr_append_mem(ds, "\n", 1);
	}
2649
      }
2650
    }
2651

2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669
    if (record)
    {
      if (!q->record_file[0] && !result_file)
	die("At line %u: Missing result file", start_lineno);
      if (!result_file)
	str_to_file(q->record_file, ds->str, ds->length);
    }
    else if (q->record_file[0])
    {
      error = check_result(ds, q->record_file, q->require_file);
    }
    if (res)
      mysql_free_result(res);
    last_result= 0;
    counter++;
  } while (!(err= mysql_next_result(mysql)));
  if (err >= 1)
    mysql_error(mysql);
2670

2671
end:
2672
  free_replace();
2673
  last_result=0;
2674 2675
  if (ds == &ds_tmp)
    dynstr_free(&ds_tmp);
2676
  if (q->type == Q_EVAL)
2677
    dynstr_free(&eval_query);
2678 2679 2680 2681 2682 2683

  /*
    We save the return code (mysql_errno(mysql)) from the last call sent
    to the server into the mysqltest builtin variable $mysql_errno. This
    variable then can be used from the test case itself.
  */
2684
  var_set_errno(mysql_errno(mysql));
2685
  DBUG_RETURN(error);
unknown's avatar
unknown committed
2686 2687 2688
}


2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033
/****************************************************************************\
 *  If --ps-protocol run ordinary statements using prepared statemnt C API
\****************************************************************************/

/*
  We don't have a mysql_stmt_send_execute() so we only handle
  complete SEND+REAP
*/

static int run_query_stmt(MYSQL *mysql, struct st_query *q, int flags)
{
  int error= 0;             /* Function return code if "goto end;" */
  int err;                  /* Temporary storage of return code from calls */
  int query_len, got_error_on_execute;
  uint num_rows;
  char *query;
  MYSQL_RES *res= NULL;     /* Note that here 'res' is meta data result set */
  DYNAMIC_STRING *ds;
  DYNAMIC_STRING ds_tmp;
  DYNAMIC_STRING eval_query;
  MYSQL_STMT *stmt;
  DBUG_ENTER("run_query_stmt");

  /*
    We must allocate a new stmt for each query in this program becasue this
    may be a new connection.
  */
  if (!(stmt= mysql_stmt_init(mysql)))
    die("At line %u: unable init stmt structure");
  
  if (q->type != Q_EVAL)
  {
    query= q->query;
    query_len= strlen(query);
  }
  else
  {
    init_dynamic_string(&eval_query, "", 16384, 65536);
    do_eval(&eval_query, q->query);
    query= eval_query.str;
    query_len= eval_query.length;
  }
  DBUG_PRINT("query", ("'%-.60s'", query));

  if (q->record_file[0])
  {
    init_dynamic_string(&ds_tmp, "", 16384, 65536);
    ds= &ds_tmp;
  }
  else
    ds= &ds_res;

  /* Store the query into the output buffer if not disabled */
  if (!disable_query_log)
  {
    replace_dynstr_append_mem(ds,query, query_len);
    dynstr_append_mem(ds, delimiter, delimiter_length);
    dynstr_append_mem(ds, "\n", 1);
  }

  /*
    We use the prepared statement interface but there is actually no
    '?' in the query. If unpreparable we fall back to use normal
    C API.
  */
  if ((err= mysql_stmt_prepare(stmt, query, query_len)) == CR_NO_PREPARE_STMT)
    return run_query_normal(mysql, q, flags);

  if (err != 0)
  {
    if (q->abort_on_error)
    {
      die("At line %u: unable to prepare statement '%s': "
          "%s (mysql_stmt_errno=%d returned=%d)",
          start_lineno, query,
          mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err);
    }
    else
    {
      /*
        Preparing is part of normal execution and some errors may be expected
      */
      error= run_query_stmt_handle_error(query, q, stmt, ds);
      goto end;
    }
  }

  /* We may have got warnings already, collect them if any */
  /* FIXME we only want this if the statement succeeds I think */ 
  run_query_stmt_handle_warnings(mysql, ds);

  /*
    No need to call mysql_stmt_bind_param() because we have no 
    parameter markers.

    To optimize performance we use a global 'stmt' that is initiated
    once. A new prepare will implicitely close the old one. When we
    terminate we will lose the connection, this also closes the last
    prepared statement.
  */

  if ((got_error_on_execute= mysql_stmt_execute(stmt)) != 0) /* 0 == Success */
  {
    if (q->abort_on_error)
    {
      /* We got an error, unexpected */
      die("At line %u: unable to execute statement '%s': "
          "%s (mysql_stmt_errno=%d returned=%d)",
          start_lineno, query, mysql_stmt_error(stmt),
          mysql_stmt_errno(stmt), got_error_on_execute);
    }
    else
    {
      /* We got an error, maybe expected */
      error= run_query_stmt_handle_error(query, q, stmt, ds);
      goto end;
    }
  }

  /*
    We instruct that we want to update the "max_length" field in
     mysql_stmt_store_result(), this is our only way to know how much
     buffer to allocate for result data
  */
  {
    my_bool one= 1;
    if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH,
                            (void*) &one) != 0)
      die("At line %u: unable to set stmt attribute "
          "'STMT_ATTR_UPDATE_MAX_LENGTH': %s (returned=%d)",
          start_lineno, query, err);
  }

  /*
    If we got here the statement succeeded and was expected to do so,
    get data. Note that this can still give errors found during execution!
  */
  if ((err= mysql_stmt_store_result(stmt)) != 0)
  {
    if (q->abort_on_error)
    {
      /* We got an error, unexpected */
      die("At line %u: unable to execute statement '%s': "
          "%s (mysql_stmt_errno=%d returned=%d)",
          start_lineno, query, mysql_stmt_error(stmt),
          mysql_stmt_errno(stmt), got_error_on_execute);
    }
    else
    {
      /* We got an error, maybe expected */
      error= run_query_stmt_handle_error(query, q, stmt, ds);
      goto end;
    }
  }

  /* If we got here the statement was both executed and read succeesfully */

  if (q->expected_errno[0].type == ERR_ERRNO &&
      q->expected_errno[0].code.errnum != 0)
  {
    verbose_msg("query '%s' succeeded - should have failed with errno %d...",
                q->query, q->expected_errno[0].code.errnum);
    error= 1;
    goto end;
  }

  num_rows= mysql_stmt_num_rows(stmt);

  /*
    Not all statements creates a result set. If there is one we can
    now create another normal result set that contains the meta
    data. This set can be handled almost like any other non prepared
    statement result set.
  */
  if (!disable_result_log && ((res= mysql_stmt_result_metadata(stmt)) != NULL))
  {
    /* Take the column count from meta info */
    MYSQL_FIELD *field= mysql_fetch_fields(res);
    uint num_fields= mysql_num_fields(res);

    /* FIXME check error from the above? */

    if (display_metadata)
      run_query_display_metadata(field, num_fields, ds);

    if (!display_result_vertically)
    {
      /* Display the table heading with the names tab separated */
      uint col_idx;
      for (col_idx= 0; col_idx < num_fields; col_idx++)
      {
        if (col_idx)
          dynstr_append_mem(ds, "\t", 1);
        replace_dynstr_append_mem(ds, field[col_idx].name,
                                  strlen(field[col_idx].name));
      }
      dynstr_append_mem(ds, "\n", 1);
    }

    /* Now we are to put the real result into the output buffer */
    /* FIXME when it works, create function append_stmt_result() */
    {
      MYSQL_BIND *bind;
      my_bool *is_null;
      unsigned long *length;
      /* FIXME we don't handle vertical display ..... */
      uint col_idx, row_idx;

      /* Allocate array with bind structs, lengths and NULL flags */
      bind= (MYSQL_BIND*)      my_malloc(num_fields * sizeof(MYSQL_BIND),
                                         MYF(MY_WME | MY_FAE));
      length= (unsigned long*) my_malloc(num_fields * sizeof(unsigned long),
                                         MYF(MY_WME | MY_FAE));
      is_null= (my_bool*)      my_malloc(num_fields * sizeof(my_bool),
                                         MYF(MY_WME | MY_FAE));

      for (col_idx= 0; col_idx < num_fields; col_idx++)
      {
        /* Allocate data for output */
        /*
          FIXME it may be a bug that for non string/blob types
          'max_length' is 0, should try out 'length' in that case
        */
        uint max_length= max(field[col_idx].max_length + 1, 1024);
        char *str_data= (char *) my_malloc(max_length, MYF(MY_WME | MY_FAE));

        bind[col_idx].buffer_type= MYSQL_TYPE_STRING;
        bind[col_idx].buffer= (char *)str_data;
        bind[col_idx].buffer_length= max_length;
        bind[col_idx].is_null= &is_null[col_idx];
        bind[col_idx].length= &length[col_idx];
      }

      /* Fill in the data into the structures created above */
      if ((err= mysql_stmt_bind_result(stmt, bind)) != 0)
        die("At line %u: unable to bind result to statement '%s': "
            "%s (mysql_stmt_errno=%d returned=%d)",
            start_lineno, query,
            mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err);

      /* Read result from each row */
      for (row_idx= 0; row_idx < num_rows; row_idx++)
      {
        if ((err= mysql_stmt_fetch(stmt)) != 0)
          die("At line %u: unable to fetch all rows from statement '%s': "
              "%s (mysql_stmt_errno=%d returned=%d)",
              start_lineno, query,
              mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err);

        /* Read result from each column */
        for (col_idx= 0; col_idx < num_fields; col_idx++)
        {
          /* FIXME is string terminated? */
          const char *val= (const char *)bind[col_idx].buffer;
          ulonglong len= *bind[col_idx].length;
          if (col_idx < max_replace_column && replace_column[col_idx])
          {
            val= replace_column[col_idx];
            len= strlen(val);
          }
          if (*bind[col_idx].is_null)
          {
            val= "NULL";
            len= 4;
          }
          if (!display_result_vertically)
          {
            if (col_idx)                      /* No tab before first col */
              dynstr_append_mem(ds, "\t", 1);
            replace_dynstr_append_mem(ds, val, len);
          }
          else
          {
            dynstr_append(ds, field[col_idx].name);
            dynstr_append_mem(ds, "\t", 1);
            replace_dynstr_append_mem(ds, val, len);
            dynstr_append_mem(ds, "\n", 1);
          }
        }
        if (!display_result_vertically)
          dynstr_append_mem(ds, "\n", 1);
      }

      if ((err= mysql_stmt_fetch(stmt)) != MYSQL_NO_DATA)
        die("At line %u: fetch didn't end with MYSQL_NO_DATA from statement "
            "'%s': %s (mysql_stmt_errno=%d returned=%d)",
            start_lineno, query,
            mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err);

      free_replace_column();

      for (col_idx= 0; col_idx < num_fields; col_idx++)
      {
        /* Free data for output */
        my_free((gptr)bind[col_idx].buffer, MYF(MY_WME | MY_FAE));
      }
      /* Free array with bind structs, lengths and NULL flags */
      my_free((gptr)bind    , MYF(MY_WME | MY_FAE));
      my_free((gptr)length  , MYF(MY_WME | MY_FAE));
      my_free((gptr)is_null , MYF(MY_WME | MY_FAE));
    }

    /* Add all warnings to the result */
    run_query_stmt_handle_warnings(mysql, ds);

    if (!disable_info)
    {
      char buf[40];
      sprintf(buf,"affected rows: %lu\n",(ulong) mysql_affected_rows(mysql));
      dynstr_append(ds, buf);
      if (mysql_info(mysql))
      {
        dynstr_append(ds, "info: ");
        dynstr_append(ds, mysql_info(mysql));
        dynstr_append_mem(ds, "\n", 1);
      }
    }
  }
  run_query_stmt_handle_warnings(mysql, ds);

  if (record)
  {
    if (!q->record_file[0] && !result_file)
      die("At line %u: Missing result file", start_lineno);
    if (!result_file)
      str_to_file(q->record_file, ds->str, ds->length);
  }
  else if (q->record_file[0])
  {
    error= check_result(ds, q->record_file, q->require_file);
  }
  if (res)
    mysql_free_result(res);     /* Free normal result set with meta data */
  last_result= 0;               /* FIXME have no idea what this is about... */

  if (err >= 1)
    mysql_error(mysql);         /* FIXME strange, has no effect... */

end:
  free_replace();
  last_result=0;
  if (ds == &ds_tmp)
    dynstr_free(&ds_tmp);
  if (q->type == Q_EVAL)
    dynstr_free(&eval_query);
3034
  var_set_errno(mysql_stmt_errno(stmt));
3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259
  mysql_stmt_close(stmt);
  DBUG_RETURN(error);
}


/****************************************************************************\
 *  Broken out sub functions to run_query_stmt()
\****************************************************************************/

static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields,
                                       DYNAMIC_STRING *ds)
{
  MYSQL_FIELD *field_end;
  dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t"
                "Column_alias\tName\tType\tLength\tMax length\tIs_null\t"
                "Flags\tDecimals\tCharsetnr\n");

  for (field_end= field+num_fields ;
       field < field_end ;
       field++)
  {
    char buff[22];
    dynstr_append_mem(ds, field->catalog,
                          field->catalog_length);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, field->db, field->db_length);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, field->org_table,
                          field->org_table_length);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, field->table,
                          field->table_length);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, field->org_name,
                          field->org_name_length);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, field->name, field->name_length);
    dynstr_append_mem(ds, "\t", 1);
    int10_to_str((int) field->type, buff, 10);
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\t", 1);
    int10_to_str((int) field->length, buff, 10);
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\t", 1);
    int10_to_str((int) field->max_length, buff, 10);
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ?
                                   "N" : "Y"), 1);
    dynstr_append_mem(ds, "\t", 1);

    int10_to_str((int) field->flags, buff, 10);
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\t", 1);
    int10_to_str((int) field->decimals, buff, 10);
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\t", 1);
    int10_to_str((int) field->charsetnr, buff, 10);
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\n", 1);
  }
}


static void run_query_stmt_handle_warnings(MYSQL *mysql, DYNAMIC_STRING *ds)
{
  uint count;
  DBUG_ENTER("run_query_stmt_handle_warnings");

  if (!disable_warnings && (count= mysql_warning_count(mysql)))
  {
    if (mysql_real_query(mysql, "SHOW WARNINGS", 13) == 0)
    {
      MYSQL_RES *warn_res= mysql_store_result(mysql);
      if (!warn_res)
        verbose_msg("Warning count is %u but didn't get any warnings\n",
                    count);
      else
      {
        dynstr_append_mem(ds, "Warnings:\n", 10);
        append_result(ds, warn_res);
        mysql_free_result(warn_res);
      }
    }
  }
  DBUG_VOID_RETURN;
}


static int run_query_stmt_handle_error(char *query, struct st_query *q,
                                       MYSQL_STMT *stmt, DYNAMIC_STRING *ds)
{
  if (q->require_file)              /* FIXME don't understand this one */
  {
    abort_not_supported_test();
  }

  if (q->abort_on_error)
    die("At line %u: query '%s' failed: %d: %s", start_lineno, query,
        mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
  else
  {
    int i;

    for (i=0 ; (uint) i < q->expected_errors ; i++)
    {
      if (((q->expected_errno[i].type == ERR_ERRNO) &&
           (q->expected_errno[i].code.errnum == mysql_stmt_errno(stmt))) ||
          ((q->expected_errno[i].type == ERR_SQLSTATE) &&
           (strcmp(q->expected_errno[i].code.sqlstate,
                   mysql_stmt_sqlstate(stmt)) == 0)))
      {
        if (i == 0 && q->expected_errors == 1)
        {
          /* Only log error if there is one possible error */
          dynstr_append_mem(ds,"ERROR ",6);
          replace_dynstr_append_mem(ds, mysql_stmt_sqlstate(stmt),
                                    strlen(mysql_stmt_sqlstate(stmt)));
          dynstr_append_mem(ds, ": ", 2);
          replace_dynstr_append_mem(ds,mysql_stmt_error(stmt),
                                    strlen(mysql_stmt_error(stmt)));
          dynstr_append_mem(ds,"\n",1);
        }
        /* Don't log error if we may not get an error */
        else if (q->expected_errno[0].type == ERR_SQLSTATE ||
                 (q->expected_errno[0].type == ERR_ERRNO &&
                  q->expected_errno[0].code.errnum != 0))
          dynstr_append(ds,"Got one of the listed errors\n");
        return 0; /* Ok */
      }
    }
    DBUG_PRINT("info",("i: %d  expected_errors: %d", i,
                       q->expected_errors));
    dynstr_append_mem(ds, "ERROR ",6);
    replace_dynstr_append_mem(ds, mysql_stmt_sqlstate(stmt),
                              strlen(mysql_stmt_sqlstate(stmt)));
    dynstr_append_mem(ds,": ",2);
    replace_dynstr_append_mem(ds, mysql_stmt_error(stmt),
                              strlen(mysql_stmt_error(stmt)));
    dynstr_append_mem(ds,"\n",1);
    if (i)
    {
      verbose_msg("query '%s' failed with wrong errno %d instead of %d...",
                  q->query, mysql_stmt_errno(stmt), q->expected_errno[0]);
      return 1; /* Error */
    }
    verbose_msg("query '%s' failed: %d: %s", q->query, mysql_stmt_errno(stmt),
                mysql_stmt_error(stmt));
    /*
      if we do not abort on error, failure to run the query does
      not fail the whole test case
    */
    return 0;
  }

  return 0;
}

/****************************************************************************\
 *  Functions to match SQL statements that can be prepared
\****************************************************************************/

static void ps_init_re(void)
{
  const char *ps_re_str =
    "^("
    "[[:space:]]*REPLACE[[:space:]]|"
    "[[:space:]]*INSERT[[:space:]]|"
    "[[:space:]]*UPDATE[[:space:]]|"
    "[[:space:]]*DELETE[[:space:]]|"
    "[[:space:]]*SELECT[[:space:]]|"
    "[[:space:]]*CREATE[[:space:]]+TABLE[[:space:]]|"
    "[[:space:]]*DO[[:space:]]|"
    "[[:space:]]*SET[[:space:]]+OPTION[[:space:]]|"
    "[[:space:]]*DELETE[[:space:]]+MULTI[[:space:]]|"
    "[[:space:]]*UPDATE[[:space:]]+MULTI[[:space:]]|"
    "[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])";

  int err= regcomp(&ps_re, ps_re_str, (REG_EXTENDED | REG_ICASE | REG_NOSUB),
                   &my_charset_latin1);
  if (err)
  {
    char erbuf[100];
    int len= regerror(err, &ps_re, erbuf, sizeof(erbuf));
    fprintf(stderr, "error %s, %d/%d `%s'\n",
            ps_eprint(err), len, (int)sizeof(erbuf), erbuf);
    exit(1);
  }
}


static int ps_match_re(char *stmt_str)
{
  int err= regexec(&ps_re, stmt_str, (size_t)0, NULL, 0);

  if (err == 0)
    return 1;
  else if (err == REG_NOMATCH)
    return 0;
  else
  {
    char erbuf[100];
    int len= regerror(err, &ps_re, erbuf, sizeof(erbuf));
    fprintf(stderr, "error %s, %d/%d `%s'\n",
            ps_eprint(err), len, (int)sizeof(erbuf), erbuf);
    exit(1);
  }
}

static char *ps_eprint(int err)
{
  static char epbuf[100];
  size_t len= regerror(REG_ITOA|err, (regex_t *)NULL, epbuf, sizeof(epbuf));
  assert(len <= sizeof(epbuf));
  return(epbuf);
}


static void ps_free_reg(void)
{
  regfree(&ps_re);
}

/****************************************************************************/

3260
void get_query_type(struct st_query* q)
3261
{
3262 3263
  char save;
  uint type;
unknown's avatar
unknown committed
3264 3265
  DBUG_ENTER("get_query_type");

3266
  if (*q->query == '}')
3267 3268
  {
    q->type = Q_END_BLOCK;
unknown's avatar
unknown committed
3269
    DBUG_VOID_RETURN;
3270 3271 3272 3273
  }
  if (q->type != Q_COMMENT_WITH_COMMAND)
    q->type = Q_QUERY;

3274 3275
  save=q->query[q->first_word_len];
  q->query[q->first_word_len]=0;
3276
  type=find_type(q->query, &command_typelib, 1+2);
3277
  q->query[q->first_word_len]=save;
3278
  if (type > 0)
unknown's avatar
unknown committed
3279
    q->type=(enum enum_commands) type;		/* Found command */
unknown's avatar
unknown committed
3280
  DBUG_VOID_RETURN;
3281
}
unknown's avatar
unknown committed
3282

unknown's avatar
unknown committed
3283

unknown's avatar
unknown committed
3284
static byte *get_var_key(const byte* var, uint* len,
unknown's avatar
unknown committed
3285
			 my_bool __attribute__((unused)) t)
unknown's avatar
unknown committed
3286 3287 3288 3289 3290 3291 3292
{
  register char* key;
  key = ((VAR*)var)->name;
  *len = ((VAR*)var)->name_len;
  return (byte*)key;
}

unknown's avatar
unknown committed
3293
static VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
unknown's avatar
unknown committed
3294 3295 3296
		     int val_len)
{
  int val_alloc_len;
unknown's avatar
unknown committed
3297
  VAR *tmp_var;
3298
  if (!name_len && name)
unknown's avatar
unknown committed
3299
    name_len = strlen(name);
3300
  if (!val_len && val)
unknown's avatar
unknown committed
3301 3302
    val_len = strlen(val) ;
  val_alloc_len = val_len + 16; /* room to grow */
3303
  if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var)
3304
						 + name_len, MYF(MY_WME))))
unknown's avatar
unknown committed
3305
    die("Out of memory");
unknown's avatar
unknown committed
3306

3307
  tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0;
3308
  tmp_var->alloced = (v == 0);
3309

3310
  if (!(tmp_var->str_val = my_malloc(val_alloc_len+1, MYF(MY_WME))))
3311
    die("Out of memory");
unknown's avatar
unknown committed
3312

unknown's avatar
unknown committed
3313
  memcpy(tmp_var->name, name, name_len);
3314 3315 3316 3317 3318
  if (val)
  {
    memcpy(tmp_var->str_val, val, val_len);
    tmp_var->str_val[val_len]=0;
  }
unknown's avatar
unknown committed
3319 3320 3321
  tmp_var->name_len = name_len;
  tmp_var->str_val_len = val_len;
  tmp_var->alloced_len = val_alloc_len;
3322
  tmp_var->int_val = (val) ? atoi(val) : 0;
unknown's avatar
unknown committed
3323 3324 3325 3326
  tmp_var->int_dirty = 0;
  return tmp_var;
}

unknown's avatar
unknown committed
3327
static void var_free(void *v)
unknown's avatar
unknown committed
3328
{
unknown's avatar
unknown committed
3329
  my_free(((VAR*) v)->str_val, MYF(MY_WME));
unknown's avatar
unknown committed
3330 3331
  if (((VAR*)v)->alloced)
   my_free((char*) v, MYF(MY_WME));
unknown's avatar
unknown committed
3332 3333 3334
}


3335
static VAR* var_from_env(const char *name, const char *def_val)
unknown's avatar
unknown committed
3336
{
unknown's avatar
unknown committed
3337 3338
  const char *tmp;
  VAR *v;
3339
  if (!(tmp = getenv(name)))
unknown's avatar
unknown committed
3340
    tmp = def_val;
unknown's avatar
unknown committed
3341

3342
  v = var_init(0, name, strlen(name), tmp, strlen(tmp));
unknown's avatar
SCRUM  
unknown committed
3343
  my_hash_insert(&var_hash, (byte*)v);
3344
  return v;
3345
}
unknown's avatar
unknown committed
3346

3347

unknown's avatar
unknown committed
3348
static void init_var_hash(MYSQL *mysql)
unknown's avatar
unknown committed
3349
{
unknown's avatar
unknown committed
3350
  VAR *v;
3351
  DBUG_ENTER("init_var_hash");
3352
  if (hash_init(&var_hash, charset_info, 
unknown's avatar
unknown committed
3353
                1024, 0, 0, get_var_key, var_free, MYF(0)))
unknown's avatar
unknown committed
3354
    die("Variable hash initialization failed");
3355 3356
  if (opt_big_test)
    my_hash_insert(&var_hash, (byte*) var_init(0,"BIG_TEST", 0, "1",0));
unknown's avatar
unknown committed
3357
  v= var_init(0,"MAX_TABLES", 0, (sizeof(ulong) == 4) ? "31" : "62",0);
unknown's avatar
SCRUM  
unknown committed
3358
  my_hash_insert(&var_hash, (byte*) v);
unknown's avatar
unknown committed
3359
  v= var_init(0,"SERVER_VERSION", 0, mysql_get_server_info(mysql), 0);
unknown's avatar
SCRUM  
unknown committed
3360
  my_hash_insert(&var_hash, (byte*) v);
unknown's avatar
unknown committed
3361 3362
  v= var_init(0,"DB", 2, db, 0);
  my_hash_insert(&var_hash, (byte*) v);
3363
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
3364
}
3365

3366

unknown's avatar
unknown committed
3367
int main(int argc, char **argv)
unknown's avatar
unknown committed
3368 3369
{
  int error = 0;
unknown's avatar
unknown committed
3370
  struct st_query *q;
unknown's avatar
unknown committed
3371
  my_bool require_file=0, q_send_flag=0;
3372
  char save_file[FN_REFLEN];
3373
  MY_INIT(argv[0]);
3374
  {
unknown's avatar
unknown committed
3375 3376
  DBUG_ENTER("main");
  DBUG_PROCESS(argv[0]);
3377

unknown's avatar
unknown committed
3378 3379 3380
  /* Use all time until exit if no explicit 'start_timer' */
  timer_start= timer_now();

3381
  save_file[0]=0;
3382
  TMPDIR[0]=0;
unknown's avatar
unknown committed
3383 3384 3385 3386
  memset(cons, 0, sizeof(cons));
  cons_end = cons + MAX_CONS;
  next_con = cons + 1;
  cur_con = cons;
unknown's avatar
unknown committed
3387

unknown's avatar
unknown committed
3388
  memset(file_stack, 0, sizeof(file_stack));
unknown's avatar
unknown committed
3389
  memset(&master_pos, 0, sizeof(master_pos));
unknown's avatar
unknown committed
3390 3391
  file_stack_end = file_stack + MAX_INCLUDE_DEPTH;
  cur_file = file_stack;
3392
  lineno   = lineno_stack;
3393
  my_init_dynamic_array(&q_lines, sizeof(struct st_query*), INIT_Q_LINES,
3394 3395 3396
		     INIT_Q_LINES);
  memset(block_stack, 0, sizeof(block_stack));
  block_stack_end = block_stack + BLOCK_STACK_DEPTH;
3397
  memset(block_ok_stack, 0, sizeof(block_stack));
3398
  cur_block = block_stack;
3399 3400
  block_ok = block_ok_stack;
  *block_ok = 1;
3401
  init_dynamic_string(&ds_res, "", 0, 65536);
unknown's avatar
unknown committed
3402
  parse_args(argc, argv);
3403 3404 3405
  if (mysql_server_init(embedded_server_arg_count,
			embedded_server_args,
			(char**) embedded_server_groups))
unknown's avatar
unknown committed
3406 3407 3408
    die("Can't initialize MySQL server");
  if (cur_file == file_stack)
    *++cur_file = stdin;
3409
  *lineno=1;
3410 3411 3412
#ifndef EMBEDDED_LIBRARY
  if (manager_host)
    init_manager();
3413
#endif
3414 3415 3416 3417 3418
  if (ps_protocol)
  {
    ps_protocol_enabled= 1;
    ps_init_re();
  }
3419
  if (!( mysql_init(&cur_con->mysql)))
unknown's avatar
unknown committed
3420
    die("Failed in mysql_init()");
unknown's avatar
unknown committed
3421 3422
  if (opt_compress)
    mysql_options(&cur_con->mysql,MYSQL_OPT_COMPRESS,NullS);
3423
  mysql_options(&cur_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
unknown's avatar
unknown committed
3424
  mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name);
unknown's avatar
unknown committed
3425

unknown's avatar
unknown committed
3426 3427 3428 3429 3430
#ifdef HAVE_OPENSSL
  if (opt_use_ssl)
    mysql_ssl_set(&cur_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
		  opt_ssl_capath, opt_ssl_cipher);
#endif
3431

unknown's avatar
unknown committed
3432
  if (!(cur_con->name = my_strdup("default", MYF(MY_WME))))
unknown's avatar
unknown committed
3433
    die("Out of memory");
unknown's avatar
unknown committed
3434

unknown's avatar
unknown committed
3435
  if (safe_connect(&cur_con->mysql, host, user, pass, db, port, unix_sock))
unknown's avatar
unknown committed
3436 3437
    die("Failed in mysql_real_connect(): %s", mysql_error(&cur_con->mysql));

unknown's avatar
unknown committed
3438 3439
  init_var_hash(&cur_con->mysql);

3440 3441 3442 3443 3444
  /*
    Initialize $mysql_errno with -1, so we can
    - distinguish it from valid values ( >= 0 ) and
    - detect if there was never a command sent to the server
  */
3445 3446
  var_set_errno(-1);

3447
  while (!read_query(&q))
3448 3449 3450 3451
  {
    int current_line_inc = 1, processed = 0;
    if (q->type == Q_UNKNOWN || q->type == Q_COMMENT_WITH_COMMAND)
      get_query_type(q);
3452
    if (*block_ok)
unknown's avatar
unknown committed
3453
    {
3454 3455 3456
      processed = 1;
      switch (q->type) {
      case Q_CONNECT: do_connect(q); break;
3457
      case Q_CONNECTION: select_connection(q->first_argument); break;
unknown's avatar
unknown committed
3458
      case Q_DISCONNECT:
3459
      case Q_DIRTY_CLOSE:
unknown's avatar
unknown committed
3460
	close_connection(q); break;
3461
      case Q_RPL_PROBE: do_rpl_probe(q); break;
3462
      case Q_ENABLE_RPL_PARSE:	 do_enable_rpl_parse(q); break;
unknown's avatar
unknown committed
3463
      case Q_DISABLE_RPL_PARSE:  do_disable_rpl_parse(q); break;
3464
      case Q_ENABLE_QUERY_LOG:	 disable_query_log=0; break;
unknown's avatar
unknown committed
3465
      case Q_DISABLE_QUERY_LOG:  disable_query_log=1; break;
3466 3467
      case Q_ENABLE_ABORT_ON_ERROR:  abort_on_error=1; break;
      case Q_DISABLE_ABORT_ON_ERROR: abort_on_error=0; break;
unknown's avatar
unknown committed
3468 3469
      case Q_ENABLE_RESULT_LOG:  disable_result_log=0; break;
      case Q_DISABLE_RESULT_LOG: disable_result_log=1; break;
3470 3471
      case Q_ENABLE_WARNINGS:    disable_warnings=0; break;
      case Q_DISABLE_WARNINGS:   disable_warnings=1; break;
3472 3473
      case Q_ENABLE_INFO:        disable_info=0; break;
      case Q_DISABLE_INFO:       disable_info=1; break;
3474
      case Q_ENABLE_METADATA:    display_metadata=1; break;
3475
      case Q_DISABLE_METADATA:   display_metadata=0; break;
3476
      case Q_SOURCE: do_source(q); break;
3477 3478
      case Q_SLEEP: do_sleep(q, 0); break;
      case Q_REAL_SLEEP: do_sleep(q, 1); break;
3479
      case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(q); break;
3480
      case Q_REQUIRE_MANAGER: do_require_manager(q); break;
3481
#ifndef EMBEDDED_LIBRARY
3482 3483
      case Q_SERVER_START: do_server_start(q); break;
      case Q_SERVER_STOP: do_server_stop(q); break;
3484
#endif
3485 3486 3487 3488
      case Q_INC: do_inc(q); break;
      case Q_DEC: do_dec(q); break;
      case Q_ECHO: do_echo(q); break;
      case Q_SYSTEM: do_system(q); break;
3489 3490 3491 3492
      case Q_DELIMITER:
	strmake(delimiter, q->first_argument, sizeof(delimiter) - 1);
	delimiter_length= strlen(delimiter);
	break;
3493
      case Q_DISPLAY_VERTICAL_RESULTS: display_result_vertically= TRUE; break;
3494
      case Q_DISPLAY_HORIZONTAL_RESULTS: 
3495
	display_result_vertically= FALSE; break;
3496
      case Q_LET: do_let(q); break;
3497
      case Q_EVAL_RESULT: eval_result = 1; break;
3498
      case Q_EVAL:
3499
	if (q->query == q->query_buf)
unknown's avatar
unknown committed
3500
        {
unknown's avatar
unknown committed
3501
	  q->query= q->first_argument;
unknown's avatar
unknown committed
3502 3503
          q->first_word_len= 0;
        }
3504
	/* fall through */
3505
      case Q_QUERY_VERTICAL:
3506
      case Q_QUERY_HORIZONTAL:
3507 3508 3509 3510 3511 3512
      {
	my_bool old_display_result_vertically= display_result_vertically;
	if (!q->query[q->first_word_len])
	{
	  /* This happens when we use 'query_..' on it's own line */
	  q_send_flag=1;
unknown's avatar
unknown committed
3513 3514 3515
          DBUG_PRINT("info",
                     ("query: '%s' first_word_len: %d  send_flag=1",
                      q->query, q->first_word_len));
3516 3517 3518 3519 3520
	  break;
	}
	/* fix up query pointer if this is * first iteration for this line */
	if (q->query == q->query_buf)
	  q->query += q->first_word_len + 1;
3521
	display_result_vertically= (q->type==Q_QUERY_VERTICAL);
unknown's avatar
unknown committed
3522
	error|= run_query(&cur_con->mysql, q, QUERY_REAP|QUERY_SEND);
3523 3524 3525
	display_result_vertically= old_display_result_vertically;
	break;
      }
3526
      case Q_QUERY:
3527
      case Q_REAP:
3528
      {
3529 3530 3531 3532 3533
	/*
	  We read the result always regardless of the mode for both full
	  query and read-result only (reap)
	*/
	int flags = QUERY_REAP;
3534
	if (q->type != Q_REAP) /* for a full query, enable the send stage */
unknown's avatar
unknown committed
3535
	  flags |= QUERY_SEND;
unknown's avatar
unknown committed
3536 3537 3538 3539 3540
	if (q_send_flag)
	{
	  flags= QUERY_SEND;
	  q_send_flag=0;
	}
3541
	if (save_file[0])
3542
	{
3543 3544 3545
	  strmov(q->record_file,save_file);
	  q->require_file=require_file;
	  save_file[0]=0;
3546
	}
unknown's avatar
unknown committed
3547
	error |= run_query(&cur_con->mysql, q, flags);
unknown's avatar
unknown committed
3548
	break;
3549
      }
unknown's avatar
unknown committed
3550
      case Q_SEND:
unknown's avatar
unknown committed
3551 3552 3553 3554 3555 3556 3557 3558
	if (!q->query[q->first_word_len])
	{
	  /* This happens when we use 'send' on it's own line */
	  q_send_flag=1;
	  break;
	}
	/* fix up query pointer if this is * first iteration for this line */
	if (q->query == q->query_buf)
3559
	  q->query += q->first_word_len;
3560 3561 3562 3563 3564
	/*
	  run_query() can execute a query partially, depending on the flags
	  QUERY_SEND flag without QUERY_REAP tells it to just send the
	  query and read the result some time later when reap instruction
	  is given on this connection.
3565
	 */
3566
	error |= run_query(&cur_con->mysql, q, QUERY_SEND);
unknown's avatar
unknown committed
3567
	break;
3568 3569 3570 3571
      case Q_RESULT:
	get_file_name(save_file,q);
	require_file=0;
	break;
unknown's avatar
unknown committed
3572
      case Q_ERROR:
3573
        global_expected_errors=get_errcodes(global_expected_errno,q);
unknown's avatar
unknown committed
3574
	break;
3575 3576 3577 3578
      case Q_REQUIRE:
	get_file_name(save_file,q);
	require_file=1;
	break;
unknown's avatar
unknown committed
3579 3580 3581
      case Q_REPLACE:
	get_replace(q);
	break;
3582 3583 3584
      case Q_REPLACE_COLUMN:
	get_replace_column(q);
	break;
3585 3586
      case Q_SAVE_MASTER_POS: do_save_master_pos(); break;
      case Q_SYNC_WITH_MASTER: do_sync_with_master(q); break;
3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599
      case Q_SYNC_SLAVE_WITH_MASTER:
      {
	do_save_master_pos();
	if (*q->first_argument)
	  select_connection(q->first_argument);
	else
	{
	  char buf[] = "slave";
	  select_connection(buf);
	}
	do_sync_with_master2("");
	break;
      }
3600
      case Q_COMMENT:				/* Ignore row */
3601
      case Q_COMMENT_WITH_COMMAND:
3602
	break;
3603 3604 3605
      case Q_PING:
	(void) mysql_ping(&cur_con->mysql);
	break;
3606
      case Q_EXEC: 
unknown's avatar
unknown committed
3607
	do_exec(q);
3608
	break;
unknown's avatar
unknown committed
3609 3610 3611 3612 3613 3614 3615 3616 3617
      case Q_START_TIMER:
	/* Overwrite possible earlier start of timer */
	timer_start= timer_now();
	break;
      case Q_END_TIMER:
	/* End timer before ending mysqltest */
	timer_output();
	got_end_timer= TRUE;
	break;
unknown's avatar
unknown committed
3618 3619 3620
      case Q_CHARACTER_SET: 
	set_charset(q);
	break;
3621 3622 3623 3624 3625 3626 3627
      case Q_DISABLE_PS_PROTOCOL:
        ps_protocol_enabled= 0;
        break;
      case Q_ENABLE_PS_PROTOCOL:
        ps_protocol_enabled= ps_protocol;
        break;

3628 3629 3630
      default: processed = 0; break;
      }
    }
3631

3632 3633 3634
    if (!processed)
    {
      current_line_inc = 0;
unknown's avatar
unknown committed
3635
      switch (q->type) {
3636 3637 3638 3639
      case Q_WHILE: do_while(q); break;
      case Q_END_BLOCK: do_done(q); break;
      default: current_line_inc = 1; break;
      }
unknown's avatar
unknown committed
3640 3641
    }

3642 3643 3644
    parser.current_line += current_line_inc;
  }

3645
  if (result_file && ds_res.length)
3646
  {
3647
    if (!record)
3648 3649
      error |= check_result(&ds_res, result_file, q->require_file);
    else
3650
      str_to_file(result_file, ds_res.str, ds_res.length);
3651
  }
3652
  dynstr_free(&ds_res);
3653

unknown's avatar
unknown committed
3654 3655
  if (!silent)
  {
3656
    if (error)
unknown's avatar
unknown committed
3657 3658 3659 3660
      printf("not ok\n");
    else
      printf("ok\n");
  }
3661

unknown's avatar
unknown committed
3662 3663
  if (!got_end_timer)
    timer_output();				/* No end_timer cmd, end it */
3664
  free_used_memory();
unknown's avatar
unknown committed
3665
  my_end(MY_CHECK_ERROR);
3666 3667
  exit(error ? 1 : 0);
  return error ? 1 : 0;				/* Keep compiler happy */
3668
  }
unknown's avatar
unknown committed
3669
}
unknown's avatar
unknown committed
3670

3671

3672 3673 3674 3675 3676 3677
/*
  Read arguments for embedded server and put them into
  embedded_server_args_count and embedded_server_args[]
*/


unknown's avatar
unknown committed
3678
static int read_server_arguments(const char *name)
3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694
{
  char argument[1024],buff[FN_REFLEN], *str=0;
  FILE *file;

  if (!test_if_hard_path(name))
  {
    strxmov(buff, opt_basedir, name, NullS);
    name=buff;
  }
  fn_format(buff,name,"","",4);

  if (!embedded_server_arg_count)
  {
    embedded_server_arg_count=1;
    embedded_server_args[0]= (char*) "";		/* Progname */
  }
unknown's avatar
unknown committed
3695
  if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716
    return 1;
  while (embedded_server_arg_count < MAX_SERVER_ARGS &&
	 (str=fgets(argument,sizeof(argument), file)))
  {
    *(strend(str)-1)=0;				/* Remove end newline */
    if (!(embedded_server_args[embedded_server_arg_count]=
	  (char*) my_strdup(str,MYF(MY_WME))))
    {
      my_fclose(file,MYF(0));
      return 1;
    }
    embedded_server_arg_count++;
  }
  my_fclose(file,MYF(0));
  if (str)
  {
    fprintf(stderr,"Too many arguments in option file: %s\n",name);
    return 1;
  }
  return 0;
}
unknown's avatar
unknown committed
3717

unknown's avatar
unknown committed
3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744
/****************************************************************************\
 *
 *  A primitive timer that give results in milliseconds if the
 *  --timer-file=<filename> is given. The timer result is written
 *  to that file when the result is available. To not confuse
 *  mysql-test-run with an old obsolete result, we remove the file
 *  before executing any commands. The time we measure is
 *
 *    - If no explicit 'start_timer' or 'end_timer' is given in the
 *      test case, the timer measure how long we execute in mysqltest.
 *
 *    - If only 'start_timer' is given we measure how long we execute
 *      from that point until we terminate mysqltest.
 *
 *    - If only 'end_timer' is given we measure how long we execute
 *      from that we enter mysqltest to the 'end_timer' is command is
 *      executed.
 *
 *    - If both 'start_timer' and 'end_timer' are given we measure
 *      the time between executing the two commands.
 *
\****************************************************************************/

static void timer_output(void)
{
  if (timer_file)
  {
3745
    char buf[32], *end;
unknown's avatar
unknown committed
3746
    ulonglong timer= timer_now() - timer_start;
3747 3748
    end= longlong2str(timer, buf, 10);
    str_to_file(timer_file,buf, (int) (end-buf));
unknown's avatar
unknown committed
3749 3750 3751 3752 3753 3754 3755 3756
  }
}

static ulonglong timer_now(void)
{
  return my_getsystime() / 10000;
}

unknown's avatar
unknown committed
3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769
/****************************************************************************
* Handle replacement of strings
****************************************************************************/

#define PC_MALLOC		256	/* Bytes for pointers */
#define PS_MALLOC		512	/* Bytes for data */

#define SPACE_CHAR	256
#define START_OF_LINE	257
#define END_OF_LINE	258
#define LAST_CHAR_CODE	259

typedef struct st_replace {
3770
  bool	 found;
unknown's avatar
unknown committed
3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875
  struct st_replace *next[256];
} REPLACE;

typedef struct st_replace_found {
  bool found;
  char *replace_string;
  uint to_offset;
  int from_offset;
} REPLACE_STRING;

#ifndef WORD_BIT
#define WORD_BIT (8*sizeof(uint))
#endif


static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name)
{
  uint i,length,old_count;
  byte *new_pos;
  const char **new_array;
  DBUG_ENTER("insert_pointer_name");

  if (! pa->typelib.count)
  {
    if (!(pa->typelib.type_names=(const char **)
	  my_malloc(((PC_MALLOC-MALLOC_OVERHEAD)/
		     (sizeof(my_string)+sizeof(*pa->flag))*
		     (sizeof(my_string)+sizeof(*pa->flag))),MYF(MY_WME))))
      DBUG_RETURN(-1);
    if (!(pa->str= (byte*) my_malloc((uint) (PS_MALLOC-MALLOC_OVERHEAD),
				     MYF(MY_WME))))
    {
      my_free((gptr) pa->typelib.type_names,MYF(0));
      DBUG_RETURN (-1);
    }
    pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(byte*)+
					       sizeof(*pa->flag));
    pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
    pa->length=0;
    pa->max_length=PS_MALLOC-MALLOC_OVERHEAD;
    pa->array_allocs=1;
  }
  length=(uint) strlen(name)+1;
  if (pa->length+length >= pa->max_length)
  {
    if (!(new_pos= (byte*) my_realloc((gptr) pa->str,
				      (uint) (pa->max_length+PS_MALLOC),
				      MYF(MY_WME))))
      DBUG_RETURN(1);
    if (new_pos != pa->str)
    {
      my_ptrdiff_t diff=PTR_BYTE_DIFF(new_pos,pa->str);
      for (i=0 ; i < pa->typelib.count ; i++)
	pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff,
					      char*);
      pa->str=new_pos;
    }
    pa->max_length+=PS_MALLOC;
  }
  if (pa->typelib.count >= pa->max_count-1)
  {
    int len;
    pa->array_allocs++;
    len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD);
    if (!(new_array=(const char **) my_realloc((gptr) pa->typelib.type_names,
					       (uint) len/
					 (sizeof(byte*)+sizeof(*pa->flag))*
					 (sizeof(byte*)+sizeof(*pa->flag)),
					 MYF(MY_WME))))
      DBUG_RETURN(1);
    pa->typelib.type_names=new_array;
    old_count=pa->max_count;
    pa->max_count=len/(sizeof(byte*) + sizeof(*pa->flag));
    pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
    memcpy((byte*) pa->flag,(my_string) (pa->typelib.type_names+old_count),
	   old_count*sizeof(*pa->flag));
  }
  pa->flag[pa->typelib.count]=0;			/* Reset flag */
  pa->typelib.type_names[pa->typelib.count++]= pa->str+pa->length;
  pa->typelib.type_names[pa->typelib.count]= NullS;	/* Put end-mark */
  VOID(strmov(pa->str+pa->length,name));
  pa->length+=length;
  DBUG_RETURN(0);
} /* insert_pointer_name */


	/* free pointer array */

void free_pointer_array(POINTER_ARRAY *pa)
{
  if (pa->typelib.count)
  {
    pa->typelib.count=0;
    my_free((gptr) pa->typelib.type_names,MYF(0));
    pa->typelib.type_names=0;
    my_free((gptr) pa->str,MYF(0));
  }
} /* free_pointer_array */


	/* Code for replace rutines */

#define SET_MALLOC_HUNC 64

typedef struct st_rep_set {
3876 3877
  uint	*bits;				/* Pointer to used sets */
  short next[LAST_CHAR_CODE];		/* Pointer to next sets */
unknown's avatar
unknown committed
3878 3879
  uint	found_len;			/* Best match to date */
  int	found_offset;
3880 3881
  uint	table_offset;
  uint	size_of_bits;			/* For convinience */
unknown's avatar
unknown committed
3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411
} REP_SET;

typedef struct st_rep_sets {
  uint		count;			/* Number of sets */
  uint		extra;			/* Extra sets in buffer */
  uint		invisible;		/* Sets not chown */
  uint		size_of_bits;
  REP_SET	*set,*set_buffer;
  uint		*bit_buffer;
} REP_SETS;

typedef struct st_found_set {
  uint table_offset;
  int found_offset;
} FOUND_SET;

typedef struct st_follow {
  int chr;
  uint table_offset;
  uint len;
} FOLLOWS;


static int init_sets(REP_SETS *sets,uint states);
static REP_SET *make_new_set(REP_SETS *sets);
static void make_sets_invisible(REP_SETS *sets);
static void free_last_set(REP_SETS *sets);
static void free_sets(REP_SETS *sets);
static void set_bit(REP_SET *set, uint bit);
static void clear_bit(REP_SET *set, uint bit);
static void or_bits(REP_SET *to,REP_SET *from);
static void copy_bits(REP_SET *to,REP_SET *from);
static int cmp_bits(REP_SET *set1,REP_SET *set2);
static int get_next_bit(REP_SET *set,uint lastpos);
static int find_set(REP_SETS *sets,REP_SET *find);
static int find_found(FOUND_SET *found_set,uint table_offset,
			  int found_offset);
static uint start_at_word(my_string pos);
static uint end_of_word(my_string pos);
static uint replace_len(my_string pos);

static uint found_sets=0;


	/* Init a replace structure for further calls */

REPLACE *init_replace(my_string *from, my_string *to,uint count,
		      my_string word_end_chars)
{
  uint i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr;
  int used_sets,chr,default_state;
  char used_chars[LAST_CHAR_CODE],is_word_end[256];
  my_string pos,to_pos,*to_array;
  REP_SETS sets;
  REP_SET *set,*start_states,*word_states,*new_set;
  FOLLOWS *follow,*follow_ptr;
  REPLACE *replace;
  FOUND_SET *found_set;
  REPLACE_STRING *rep_str;
  DBUG_ENTER("init_replace");

  /* Count number of states */
  for (i=result_len=max_length=0 , states=2 ; i < count ; i++)
  {
    len=replace_len(from[i]);
    if (!len)
    {
      errno=EINVAL;
      my_message(0,"No to-string for last from-string",MYF(ME_BELL));
      DBUG_RETURN(0);
    }
    states+=len+1;
    result_len+=(uint) strlen(to[i])+1;
    if (len > max_length)
      max_length=len;
  }
  bzero((char*) is_word_end,sizeof(is_word_end));
  for (i=0 ; word_end_chars[i] ; i++)
    is_word_end[(uchar) word_end_chars[i]]=1;

  if (init_sets(&sets,states))
    DBUG_RETURN(0);
  found_sets=0;
  if (!(found_set= (FOUND_SET*) my_malloc(sizeof(FOUND_SET)*max_length*count,
					  MYF(MY_WME))))
  {
    free_sets(&sets);
    DBUG_RETURN(0);
  }
  VOID(make_new_set(&sets));			/* Set starting set */
  make_sets_invisible(&sets);			/* Hide previus sets */
  used_sets=-1;
  word_states=make_new_set(&sets);		/* Start of new word */
  start_states=make_new_set(&sets);		/* This is first state */
  if (!(follow=(FOLLOWS*) my_malloc((states+2)*sizeof(FOLLOWS),MYF(MY_WME))))
  {
    free_sets(&sets);
    my_free((gptr) found_set,MYF(0));
    DBUG_RETURN(0);
  }

	/* Init follow_ptr[] */
  for (i=0, states=1, follow_ptr=follow+1 ; i < count ; i++)
  {
    if (from[i][0] == '\\' && from[i][1] == '^')
    {
      set_bit(start_states,states+1);
      if (!from[i][2])
      {
	start_states->table_offset=i;
	start_states->found_offset=1;
      }
    }
    else if (from[i][0] == '\\' && from[i][1] == '$')
    {
      set_bit(start_states,states);
      set_bit(word_states,states);
      if (!from[i][2] && start_states->table_offset == (uint) ~0)
      {
	start_states->table_offset=i;
	start_states->found_offset=0;
      }
    }
    else
    {
      set_bit(word_states,states);
      if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2]))
	set_bit(start_states,states+1);
      else
	set_bit(start_states,states);
    }
    for (pos=from[i], len=0; *pos ; pos++)
    {
      if (*pos == '\\' && *(pos+1))
      {
	pos++;
	switch (*pos) {
	case 'b':
	  follow_ptr->chr = SPACE_CHAR;
	  break;
	case '^':
	  follow_ptr->chr = START_OF_LINE;
	  break;
	case '$':
	  follow_ptr->chr = END_OF_LINE;
	  break;
	case 'r':
	  follow_ptr->chr = '\r';
	  break;
	case 't':
	  follow_ptr->chr = '\t';
	  break;
	case 'v':
	  follow_ptr->chr = '\v';
	  break;
	default:
	  follow_ptr->chr = (uchar) *pos;
	  break;
	}
      }
      else
	follow_ptr->chr= (uchar) *pos;
      follow_ptr->table_offset=i;
      follow_ptr->len= ++len;
      follow_ptr++;
    }
    follow_ptr->chr=0;
    follow_ptr->table_offset=i;
    follow_ptr->len=len;
    follow_ptr++;
    states+=(uint) len+1;
  }


  for (set_nr=0,pos=0 ; set_nr < sets.count ; set_nr++)
  {
    set=sets.set+set_nr;
    default_state= 0;				/* Start from beginning */

    /* If end of found-string not found or start-set with current set */

    for (i= (uint) ~0; (i=get_next_bit(set,i)) ;)
    {
      if (!follow[i].chr)
      {
	if (! default_state)
	  default_state= find_found(found_set,set->table_offset,
				    set->found_offset+1);
      }
    }
    copy_bits(sets.set+used_sets,set);		/* Save set for changes */
    if (!default_state)
      or_bits(sets.set+used_sets,sets.set);	/* Can restart from start */

    /* Find all chars that follows current sets */
    bzero((char*) used_chars,sizeof(used_chars));
    for (i= (uint) ~0; (i=get_next_bit(sets.set+used_sets,i)) ;)
    {
      used_chars[follow[i].chr]=1;
      if ((follow[i].chr == SPACE_CHAR && !follow[i+1].chr &&
	   follow[i].len > 1) || follow[i].chr == END_OF_LINE)
	used_chars[0]=1;
    }

    /* Mark word_chars used if \b is in state */
    if (used_chars[SPACE_CHAR])
      for (pos= word_end_chars ; *pos ; pos++)
	used_chars[(int) (uchar) *pos] = 1;

    /* Handle other used characters */
    for (chr= 0 ; chr < 256 ; chr++)
    {
      if (! used_chars[chr])
	set->next[chr]= chr ? default_state : -1;
      else
      {
	new_set=make_new_set(&sets);
	set=sets.set+set_nr;			/* if realloc */
	new_set->table_offset=set->table_offset;
	new_set->found_len=set->found_len;
	new_set->found_offset=set->found_offset+1;
	found_end=0;

	for (i= (uint) ~0 ; (i=get_next_bit(sets.set+used_sets,i)) ; )
	{
	  if (!follow[i].chr || follow[i].chr == chr ||
	      (follow[i].chr == SPACE_CHAR &&
	       (is_word_end[chr] ||
		(!chr && follow[i].len > 1 && ! follow[i+1].chr))) ||
	      (follow[i].chr == END_OF_LINE && ! chr))
	  {
	    if ((! chr || (follow[i].chr && !follow[i+1].chr)) &&
		follow[i].len > found_end)
	      found_end=follow[i].len;
	    if (chr && follow[i].chr)
	      set_bit(new_set,i+1);		/* To next set */
	    else
	      set_bit(new_set,i);
	  }
	}
	if (found_end)
	{
	  new_set->found_len=0;			/* Set for testing if first */
	  bits_set=0;
	  for (i= (uint) ~0; (i=get_next_bit(new_set,i)) ;)
	  {
	    if ((follow[i].chr == SPACE_CHAR ||
		 follow[i].chr == END_OF_LINE) && ! chr)
	      bit_nr=i+1;
	    else
	      bit_nr=i;
	    if (follow[bit_nr-1].len < found_end ||
		(new_set->found_len &&
		 (chr == 0 || !follow[bit_nr].chr)))
	      clear_bit(new_set,i);
	    else
	    {
	      if (chr == 0 || !follow[bit_nr].chr)
	      {					/* best match  */
		new_set->table_offset=follow[bit_nr].table_offset;
		if (chr || (follow[i].chr == SPACE_CHAR ||
			    follow[i].chr == END_OF_LINE))
		  new_set->found_offset=found_end;	/* New match */
		new_set->found_len=found_end;
	      }
	      bits_set++;
	    }
	  }
	  if (bits_set == 1)
	  {
	    set->next[chr] = find_found(found_set,
					new_set->table_offset,
					new_set->found_offset);
	    free_last_set(&sets);
	  }
	  else
	    set->next[chr] = find_set(&sets,new_set);
	}
	else
	  set->next[chr] = find_set(&sets,new_set);
      }
    }
  }

	/* Alloc replace structure for the replace-state-machine */

  if ((replace=(REPLACE*) my_malloc(sizeof(REPLACE)*(sets.count)+
				    sizeof(REPLACE_STRING)*(found_sets+1)+
				    sizeof(my_string)*count+result_len,
				    MYF(MY_WME | MY_ZEROFILL))))
  {
    rep_str=(REPLACE_STRING*) (replace+sets.count);
    to_array=(my_string*) (rep_str+found_sets+1);
    to_pos=(my_string) (to_array+count);
    for (i=0 ; i < count ; i++)
    {
      to_array[i]=to_pos;
      to_pos=strmov(to_pos,to[i])+1;
    }
    rep_str[0].found=1;
    rep_str[0].replace_string=0;
    for (i=1 ; i <= found_sets ; i++)
    {
      pos=from[found_set[i-1].table_offset];
      rep_str[i].found= !bcmp(pos,"\\^",3) ? 2 : 1;
      rep_str[i].replace_string=to_array[found_set[i-1].table_offset];
      rep_str[i].to_offset=found_set[i-1].found_offset-start_at_word(pos);
      rep_str[i].from_offset=found_set[i-1].found_offset-replace_len(pos)+
	end_of_word(pos);
    }
    for (i=0 ; i < sets.count ; i++)
    {
      for (j=0 ; j < 256 ; j++)
	if (sets.set[i].next[j] >= 0)
	  replace[i].next[j]=replace+sets.set[i].next[j];
	else
	  replace[i].next[j]=(REPLACE*) (rep_str+(-sets.set[i].next[j]-1));
    }
  }
  my_free((gptr) follow,MYF(0));
  free_sets(&sets);
  my_free((gptr) found_set,MYF(0));
  DBUG_PRINT("exit",("Replace table has %d states",sets.count));
  DBUG_RETURN(replace);
}


static int init_sets(REP_SETS *sets,uint states)
{
  bzero((char*) sets,sizeof(*sets));
  sets->size_of_bits=((states+7)/8);
  if (!(sets->set_buffer=(REP_SET*) my_malloc(sizeof(REP_SET)*SET_MALLOC_HUNC,
					      MYF(MY_WME))))
    return 1;
  if (!(sets->bit_buffer=(uint*) my_malloc(sizeof(uint)*sets->size_of_bits*
					   SET_MALLOC_HUNC,MYF(MY_WME))))
  {
    my_free((gptr) sets->set,MYF(0));
    return 1;
  }
  return 0;
}

	/* Make help sets invisible for nicer codeing */

static void make_sets_invisible(REP_SETS *sets)
{
  sets->invisible=sets->count;
  sets->set+=sets->count;
  sets->count=0;
}

static REP_SET *make_new_set(REP_SETS *sets)
{
  uint i,count,*bit_buffer;
  REP_SET *set;
  if (sets->extra)
  {
    sets->extra--;
    set=sets->set+ sets->count++;
    bzero((char*) set->bits,sizeof(uint)*sets->size_of_bits);
    bzero((char*) &set->next[0],sizeof(set->next[0])*LAST_CHAR_CODE);
    set->found_offset=0;
    set->found_len=0;
    set->table_offset= (uint) ~0;
    set->size_of_bits=sets->size_of_bits;
    return set;
  }
  count=sets->count+sets->invisible+SET_MALLOC_HUNC;
  if (!(set=(REP_SET*) my_realloc((gptr) sets->set_buffer,
				   sizeof(REP_SET)*count,
				  MYF(MY_WME))))
    return 0;
  sets->set_buffer=set;
  sets->set=set+sets->invisible;
  if (!(bit_buffer=(uint*) my_realloc((gptr) sets->bit_buffer,
				      (sizeof(uint)*sets->size_of_bits)*count,
				      MYF(MY_WME))))
    return 0;
  sets->bit_buffer=bit_buffer;
  for (i=0 ; i < count ; i++)
  {
    sets->set_buffer[i].bits=bit_buffer;
    bit_buffer+=sets->size_of_bits;
  }
  sets->extra=SET_MALLOC_HUNC;
  return make_new_set(sets);
}

static void free_last_set(REP_SETS *sets)
{
  sets->count--;
  sets->extra++;
  return;
}

static void free_sets(REP_SETS *sets)
{
  my_free((gptr)sets->set_buffer,MYF(0));
  my_free((gptr)sets->bit_buffer,MYF(0));
  return;
}

static void set_bit(REP_SET *set, uint bit)
{
  set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
  return;
}

static void clear_bit(REP_SET *set, uint bit)
{
  set->bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT));
  return;
}


static void or_bits(REP_SET *to,REP_SET *from)
{
  reg1 uint i;
  for (i=0 ; i < to->size_of_bits ; i++)
    to->bits[i]|=from->bits[i];
  return;
}

static void copy_bits(REP_SET *to,REP_SET *from)
{
  memcpy((byte*) to->bits,(byte*) from->bits,
	 (size_t) (sizeof(uint) * to->size_of_bits));
}

static int cmp_bits(REP_SET *set1,REP_SET *set2)
{
  return bcmp((byte*) set1->bits,(byte*) set2->bits,
	      sizeof(uint) * set1->size_of_bits);
}


	/* Get next set bit from set. */

static int get_next_bit(REP_SET *set,uint lastpos)
{
  uint pos,*start,*end,bits;

  start=set->bits+ ((lastpos+1) / WORD_BIT);
  end=set->bits + set->size_of_bits;
  bits=start[0] & ~((1 << ((lastpos+1) % WORD_BIT)) -1);

  while (! bits && ++start < end)
    bits=start[0];
  if (!bits)
    return 0;
  pos=(uint) (start-set->bits)*WORD_BIT;
  while (! (bits & 1))
  {
    bits>>=1;
    pos++;
  }
  return pos;
}

	/* find if there is a same set in sets. If there is, use it and
	   free given set, else put in given set in sets and return it's
	   position */

static int find_set(REP_SETS *sets,REP_SET *find)
{
  uint i;
  for (i=0 ; i < sets->count-1 ; i++)
  {
    if (!cmp_bits(sets->set+i,find))
    {
      free_last_set(sets);
      return i;
    }
  }
  return i;				/* return new postion */
}

	/* find if there is a found_set with same table_offset & found_offset
	   If there is return offset to it, else add new offset and return pos.
	   Pos returned is -offset-2 in found_set_structure because it's is
	   saved in set->next and set->next[] >= 0 points to next set and
	   set->next[] == -1 is reserved for end without replaces.
	   */

static int find_found(FOUND_SET *found_set,uint table_offset, int found_offset)
{
  int i;
  for (i=0 ; (uint) i < found_sets ; i++)
    if (found_set[i].table_offset == table_offset &&
	found_set[i].found_offset == found_offset)
      return -i-2;
  found_set[i].table_offset=table_offset;
  found_set[i].found_offset=found_offset;
  found_sets++;
  return -i-2;				/* return new postion */
}

	/* Return 1 if regexp starts with \b or ends with \b*/

static uint start_at_word(my_string pos)
{
  return (((!bcmp(pos,"\\b",2) && pos[2]) || !bcmp(pos,"\\^",2)) ? 1 : 0);
}

static uint end_of_word(my_string pos)
{
  my_string end=strend(pos);
  return ((end > pos+2 && !bcmp(end-2,"\\b",2)) ||
	  (end >= pos+2 && !bcmp(end-2,"\\$",2))) ?
	    1 : 0;
}


static uint replace_len(my_string str)
{
  uint len=0;
  while (*str)
  {
    if (str[0] == '\\' && str[1])
      str++;
    str++;
    len++;
  }
  return len;
}


	/* Replace strings;  Return length of result string */

4412
uint replace_strings(REPLACE *rep, my_string *start,uint *max_length,
unknown's avatar
unknown committed
4413
		     const char *from)
unknown's avatar
unknown committed
4414 4415 4416
{
  reg1 REPLACE *rep_pos;
  reg2 REPLACE_STRING *rep_str;
unknown's avatar
unknown committed
4417
  my_string to,end,pos,new_str;
unknown's avatar
unknown committed
4418 4419 4420

  end=(to= *start) + *max_length-1;
  rep_pos=rep+1;
4421
  for (;;)
unknown's avatar
unknown committed
4422 4423 4424 4425 4426 4427 4428
  {
    while (!rep_pos->found)
    {
      rep_pos= rep_pos->next[(uchar) *from];
      if (to == end)
      {
	(*max_length)+=8192;
unknown's avatar
unknown committed
4429
	if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME))))
unknown's avatar
unknown committed
4430
	  return (uint) -1;
unknown's avatar
unknown committed
4431 4432
	to=new_str+(to - *start);
	end=(*start=new_str)+ *max_length-1;
unknown's avatar
unknown committed
4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443
      }
      *to++= *from++;
    }
    if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string)
      return (uint) (to - *start)-1;
    to-=rep_str->to_offset;
    for (pos=rep_str->replace_string; *pos ; pos++)
    {
      if (to == end)
      {
	(*max_length)*=2;
unknown's avatar
unknown committed
4444
	if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME))))
unknown's avatar
unknown committed
4445
	  return (uint) -1;
unknown's avatar
unknown committed
4446 4447
	to=new_str+(to - *start);
	end=(*start=new_str)+ *max_length-1;
unknown's avatar
unknown committed
4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468
      }
      *to++= *pos;
    }
    if (!*(from-=rep_str->from_offset) && rep_pos->found != 2)
      return (uint) (to - *start);
    rep_pos=rep;
  }
}

static int initialize_replace_buffer(void)
{
  out_length=8192;
  if (!(out_buff=my_malloc(out_length,MYF(MY_WME))))
    return(1);
  return 0;
}

static void free_replace_buffer(void)
{
  my_free(out_buff,MYF(MY_WME));
}
4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525


/****************************************************************************
 Replace results for a column
*****************************************************************************/

static void free_replace_column()
{
  uint i;
  for (i=0 ; i < max_replace_column ; i++)
  {
    if (replace_column[i])
    {
      my_free(replace_column[i], 0);
      replace_column[i]= 0;
    }
  }
  max_replace_column= 0;
}

/*
  Get arguments for replace_columns. The syntax is:
  replace-column column_number to_string [column_number to_string ...]
  Where each argument may be quoted with ' or "
  A argument may also be a variable, in which case the value of the
  variable is replaced.
*/

static void get_replace_column(struct st_query *q)
{
  char *from=q->first_argument;
  char *buff,*start;
  DBUG_ENTER("get_replace_columns");

  free_replace_column();
  if (!*from)
    die("Missing argument in %s\n", q->query);

  /* Allocate a buffer for results */
  start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
  while (*from)
  {
    char *to;
    uint column_number;

    to= get_string(&buff, &from, q);
    if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS)
      die("Wrong column number to replace_columns in %s\n", q->query);
    if (!*from)
      die("Wrong number of arguments to replace in %s\n", q->query);
    to= get_string(&buff, &from, q);
    my_free(replace_column[column_number-1], MY_ALLOW_ZERO_PTR);
    replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE));
    set_if_bigger(max_replace_column, column_number);
  }
  my_free(start, MYF(0));
}
unknown's avatar
unknown committed
4526

unknown's avatar
unknown committed
4527
#if defined(__NETWARE__) || defined(__WIN__)
unknown's avatar
unknown committed
4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548
/*
  Substitute environment variables with text.

  SYNOPSIS
    subst_env_var()
    arg			String that should be substitute

  DESCRIPTION
    This function takes a string as an input and replaces the
    environment variables, that starts with '$' character, with it value.

  NOTES
    Return string must be freed with my_free()

  RETURN
    String with environment variables replaced.
*/

static char *subst_env_var(const char *str)
{
  char *result;
4549
  char *pos;
unknown's avatar
unknown committed
4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568

  result= pos= my_malloc(MAX_QUERY, MYF(MY_FAE));
  while (*str)
  {
    /*
      need this only when we want to provide the functionality of
      escaping through \ 'backslash'
      if ((result == pos && *str=='$') ||
          (result != pos && *str=='$' && str[-1] !='\\'))
    */
    if (*str == '$')
    {
      char env_var[256], *env_pos= env_var, *subst;

      /* Search for end of environment variable */
      for (str++;
           *str && !isspace(*str) && *str != '\\' && *str != '/' &&
             *str != '$';
           str++)
4569
        *env_pos++= *str;
unknown's avatar
unknown committed
4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610
      *env_pos= 0;

      if (!(subst= getenv(env_var)))
      {
        my_free(result, MYF(0));
        die("MYSQLTEST.NLM: Environment variable %s is not defined\n",
            env_var);
      }

      /* get the string to be substitued for env_var  */
      pos= strmov(pos, subst);
      /* Process delimiter in *str again */
    }
    else
      *pos++= *str++;
  }
  *pos= 0;
  return result;
}


/*
  popen replacement for Netware

  SYNPOSIS
    my_popen()
    name		Command to execute (with possible env variables)
    mode		Mode for popen.

  NOTES
    Environment variable expansion does not take place for popen function
    on NetWare, so we use this function to wrap around popen to do this.

    For the moment we ignore 'mode' and always use 'r0'

  RETURN
    # >= 0	File handle
    -1		Error
*/

#undef popen                                    /* Remove wrapper */
unknown's avatar
unknown committed
4611 4612 4613
#ifdef __WIN__ 
#define popen _popen                           /* redefine for windows */
#endif
unknown's avatar
unknown committed
4614

4615
FILE *my_popen(const char *cmd, const char *mode __attribute__((unused)))
unknown's avatar
unknown committed
4616 4617
{
  char *subst_cmd;
4618 4619
  FILE *res_file;

unknown's avatar
unknown committed
4620 4621 4622 4623 4624 4625
  subst_cmd= subst_env_var(cmd);
  res_file= popen(subst_cmd, "r0");
  my_free(subst_cmd, MYF(0));
  return res_file;
}

unknown's avatar
unknown committed
4626
#endif /* __NETWARE__ or  __WIN__*/