mysqltest.c 151 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.6"
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>
unknown's avatar
unknown committed
62
#include "my_regex.h"                     /* Our own version of lib */
unknown's avatar
unknown committed
63
#ifdef HAVE_SYS_WAIT_H
64
#include <sys/wait.h>
unknown's avatar
unknown committed
65 66
#endif
#ifndef WEXITSTATUS
unknown's avatar
unknown committed
67 68 69 70 71
# ifdef __WIN__
#  define WEXITSTATUS(stat_val) (stat_val)
# else
#  define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
# endif
unknown's avatar
unknown committed
72
#endif
73 74
/* MAX_QUERY is 256K -- there is a test in sp-big that is >128K */
#define MAX_QUERY     (256*1024)
unknown's avatar
unknown committed
75
#define MAX_VAR_NAME	256
76 77
#define MAX_COLUMNS	256
#define MAX_CONS	128
unknown's avatar
unknown committed
78
#define MAX_INCLUDE_DEPTH 16
79 80
#define INIT_Q_LINES	  1024
#define MIN_VAR_ALLOC	  32
81
#define BLOCK_STACK_DEPTH  32
82
#define MAX_EXPECTED_ERRORS 10
unknown's avatar
unknown committed
83 84
#define QUERY_SEND  1
#define QUERY_REAP  2
85 86 87
#ifndef MYSQL_MANAGER_PORT
#define MYSQL_MANAGER_PORT 23546
#endif
unknown's avatar
unknown committed
88
#define MAX_SERVER_ARGS 64
89

unknown's avatar
unknown committed
90

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

95 96 97
#define RESULT_OK 0
#define RESULT_CONTENT_MISMATCH 1
#define RESULT_LENGTH_MISMATCH 2
98

99
enum {OPT_MANAGER_USER=256,OPT_MANAGER_HOST,OPT_MANAGER_PASSWD,
unknown's avatar
unknown committed
100 101
      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,
102
      OPT_SSL_CIPHER,OPT_PS_PROTOCOL,OPT_SP_PROTOCOL,OPT_CURSOR_PROTOCOL,
103
      OPT_VIEW_PROTOCOL, OPT_SSL_VERIFY_SERVER_CERT, OPT_MAX_CONNECT_RETRIES};
unknown's avatar
unknown committed
104

105 106
/* ************************************************************************ */
/*
107 108 109
  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
unknown's avatar
unknown committed
110 111
  set to type ERR_EMPTY. When an SQL statement returns an error, we use
  this list to check if this is an expected error.
112
*/
unknown's avatar
unknown committed
113

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
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;

131 132 133 134 135 136
typedef struct
{
  const char *name;
  long        code;
} st_error;

137 138
static st_error global_error[] =
{
139 140 141 142
#include <mysqld_ername.h>
  { 0, 0 }
};

143 144 145 146
static match_err global_expected_errno[MAX_EXPECTED_ERRORS];
static uint global_expected_errors;

/* ************************************************************************ */
unknown's avatar
unknown committed
147

148
static int record = 0, opt_sleep=0;
unknown's avatar
unknown committed
149
static char *db = 0, *pass=0;
150
const char *user = 0, *host = 0, *unix_sock = 0, *opt_basedir="./";
151
static int port = 0;
152
static int opt_max_connect_retries;
153
static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0;
154 155 156 157 158
static my_bool tty_password= 0;
static my_bool ps_protocol= 0, ps_protocol_enabled= 0;
static my_bool sp_protocol= 0, sp_protocol_enabled= 0;
static my_bool view_protocol= 0, view_protocol_enabled= 0;
static my_bool cursor_protocol= 0, cursor_protocol_enabled= 0;
159
static int parsing_disabled= 0;
160
const char *manager_user="root",*manager_host=0;
161 162 163 164
char *manager_pass=0;
int manager_port=MYSQL_MANAGER_PORT;
int manager_wait_timeout=3;
MYSQL_MANAGER* manager=0;
165

166
static char **default_argv;
unknown's avatar
unknown committed
167
static const char *load_default_groups[]= { "mysqltest","client",0 };
unknown's avatar
unknown committed
168
static char line_buffer[MAX_DELIMITER], *line_buffer_pos= line_buffer;
unknown's avatar
unknown committed
169

170 171 172 173
typedef struct
{
  FILE* file;
  const char *file_name;
174
  uint lineno; /* Current line in file */
175 176 177 178 179
} test_file;

static test_file file_stack[MAX_INCLUDE_DEPTH];
static test_file* cur_file;
static test_file* file_stack_end;
180
uint start_lineno; /* Start line of query */
181

unknown's avatar
unknown committed
182
static char TMPDIR[FN_REFLEN];
183 184
static char delimiter[MAX_DELIMITER]= DEFAULT_DELIMITER;
static uint delimiter_length= 1;
unknown's avatar
unknown committed
185

unknown's avatar
unknown committed
186 187 188 189 190 191 192 193 194 195
/* Block stack */
enum block_cmd { cmd_none, cmd_if, cmd_while };
typedef struct
{
  int             line; /* Start line of block */
  my_bool         ok;   /* Should block be executed */
  enum block_cmd  cmd;  /* Command owning the block */
} BLOCK;
static BLOCK block_stack[BLOCK_STACK_DEPTH];
static BLOCK *cur_block, *block_stack_end;
196

unknown's avatar
unknown committed
197
static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */
198
static const char *charset_name= "latin1"; /* Default character set name */
199

200 201 202
static int embedded_server_arg_count=0;
static char *embedded_server_args[MAX_SERVER_ARGS];

203
static my_bool display_result_vertically= FALSE, display_metadata= FALSE;
204

unknown's avatar
unknown committed
205 206 207 208 209 210 211
/* 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);

212 213 214 215 216 217 218 219
/* Precompiled re's */
static my_regex_t ps_re;     /* the query can be run using PS protocol */
static my_regex_t sp_re;     /* the query can be run as a SP */
static my_regex_t view_re;   /* the query can be run as a view*/

static void init_re(void);
static int match_re(my_regex_t *, char *);
static void free_re(void);
220

221 222
static const char *embedded_server_groups[]=
{
223 224 225 226 227 228
  "server",
  "embedded",
  "mysqltest_SERVER",
  NullS
};

229 230
DYNAMIC_ARRAY q_lines;

unknown's avatar
unknown committed
231 232
#include "sslopt-vars.h"

233
typedef struct
unknown's avatar
unknown committed
234 235 236 237 238
{
  char file[FN_REFLEN];
  ulong pos;
} MASTER_POS ;

unknown's avatar
unknown committed
239 240 241
struct connection
{
  MYSQL mysql;
242 243
  /* Used when creating views and sp, to avoid implicit commit */
  MYSQL* util_mysql;
unknown's avatar
unknown committed
244
  char *name;
245
  MYSQL_STMT* stmt;
unknown's avatar
unknown committed
246 247
};

unknown's avatar
unknown committed
248 249 250 251
typedef struct
{
  int read_lines,current_line;
} PARSER;
252 253

PARSER parser;
unknown's avatar
unknown committed
254
MASTER_POS master_pos;
unknown's avatar
unknown committed
255 256
/* if set, all results are concated and compared against this file */
const char *result_file = 0;
257

258
typedef struct
259
{
unknown's avatar
unknown committed
260
  char *name;
unknown's avatar
unknown committed
261
  int name_len;
unknown's avatar
unknown committed
262
  char *str_val;
263 264 265 266
  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
267
  int alloced;
268 269 270 271
} VAR;

VAR var_reg[10];
/*Perl/shell-like variable registers */
unknown's avatar
unknown committed
272
HASH var_hash;
273
my_bool disable_query_log=0, disable_result_log=0, disable_warnings=0;
274
my_bool disable_ps_warnings= 0;
275
my_bool disable_info= 1;			/* By default off */
276
my_bool abort_on_error= 1;
277

unknown's avatar
unknown committed
278 279 280
struct connection cons[MAX_CONS];
struct connection* cur_con, *next_con, *cons_end;

unknown's avatar
unknown committed
281 282 283
  /* Add new commands before Q_UNKNOWN !*/

enum enum_commands {
284
Q_CONNECTION=1,     Q_QUERY,
unknown's avatar
unknown committed
285
Q_CONNECT,	    Q_SLEEP, Q_REAL_SLEEP,
286 287 288 289 290 291
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,
292 293 294
Q_SYNC_WITH_MASTER,
Q_SYNC_SLAVE_WITH_MASTER,
Q_ERROR,
295
Q_SEND,		    Q_REAP,
296
Q_DIRTY_CLOSE,	    Q_REPLACE, Q_REPLACE_COLUMN,
297 298
Q_PING,		    Q_EVAL,
Q_RPL_PROBE,	    Q_ENABLE_RPL_PARSE,
299
Q_DISABLE_RPL_PARSE, Q_EVAL_RESULT,
unknown's avatar
unknown committed
300
Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
unknown's avatar
unknown committed
301
Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
302
Q_SERVER_START, Q_SERVER_STOP,Q_REQUIRE_MANAGER,
303
Q_WAIT_FOR_SLAVE_TO_STOP,
304
Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
305
Q_ENABLE_PS_WARNINGS, Q_DISABLE_PS_WARNINGS,
306
Q_ENABLE_INFO, Q_DISABLE_INFO,
307
Q_ENABLE_METADATA, Q_DISABLE_METADATA,
308
Q_EXEC, Q_DELIMITER,
309
Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR,
310
Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
unknown's avatar
unknown committed
311
Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL,
unknown's avatar
unknown committed
312
Q_START_TIMER, Q_END_TIMER,
313
Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL,
unknown's avatar
unknown committed
314
Q_EXIT,
315
Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT,
unknown's avatar
unknown committed
316
Q_IF,
317
Q_DISABLE_PARSING, Q_ENABLE_PARSING,
318

319 320
Q_UNKNOWN,			       /* Unknown command.   */
Q_COMMENT,			       /* Comments, ignored. */
321
Q_COMMENT_WITH_COMMAND
unknown's avatar
unknown committed
322 323
};

324
/* this should really be called command */
325
struct st_query
unknown's avatar
unknown committed
326
{
327
  char *query, *query_buf,*first_argument,*last_argument,*end;
unknown's avatar
unknown committed
328
  int first_word_len;
329
  my_bool abort_on_error, require_file;
330
  match_err expected_errno[MAX_EXPECTED_ERRORS];
unknown's avatar
unknown committed
331
  uint expected_errors;
unknown's avatar
unknown committed
332
  char record_file[FN_REFLEN];
unknown's avatar
unknown committed
333
  enum enum_commands type;
unknown's avatar
unknown committed
334 335
};

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
const char *command_names[]=
{
  "connection",
  "query",
  "connect",
  "sleep",
  "real_sleep",
  "inc",
  "dec",
  "source",
  "disconnect",
  "let",
  "echo",
  "while",
  "end",
  "system",
  "result",
  "require",
  "save_master_pos",
  "sync_with_master",
356
  "sync_slave_with_master",
357 358 359 360 361
  "error",
  "send",
  "reap",
  "dirty_close",
  "replace_result",
362
  "replace_column",
363 364 365 366 367 368
  "ping",
  "eval",
  "rpl_probe",
  "enable_rpl_parse",
  "disable_rpl_parse",
  "eval_result",
unknown's avatar
unknown committed
369
  /* Enable/disable that the _query_ is logged to result file */
370 371
  "enable_query_log",
  "disable_query_log",
unknown's avatar
unknown committed
372
  /* Enable/disable that the _result_ from a query is logged to result file */
373 374 375 376 377 378
  "enable_result_log",
  "disable_result_log",
  "server_start",
  "server_stop",
  "require_manager",
  "wait_for_slave_to_stop",
379 380
  "enable_warnings",
  "disable_warnings",
381 382
  "enable_ps_warnings",
  "disable_ps_warnings",
383
  "enable_info",
384
  "disable_info",
385 386
  "enable_metadata",
  "disable_metadata",
387
  "exec",
388
  "delimiter",
389 390
  "disable_abort_on_error",
  "enable_abort_on_error",
391
  "vertical_results",
392
  "horizontal_results",
393
  "query_vertical",
394
  "query_horizontal",
unknown's avatar
unknown committed
395 396
  "start_timer",
  "end_timer",
unknown's avatar
unknown committed
397
  "character_set",
398 399
  "disable_ps_protocol",
  "enable_ps_protocol",
unknown's avatar
unknown committed
400
  "exit",
401 402
  "disable_reconnect",
  "enable_reconnect",
unknown's avatar
unknown committed
403
  "if",
404 405
  "disable_parsing",
  "enable_parsing",
unknown's avatar
unknown committed
406
  0
407 408 409
};

TYPELIB command_typelib= {array_elements(command_names),"",
unknown's avatar
unknown committed
410
			  command_names, 0};
411

412
DYNAMIC_STRING ds_res;
unknown's avatar
unknown committed
413
static void die(const char *fmt, ...);
unknown's avatar
unknown committed
414
static void init_var_hash();
415
static VAR* var_from_env(const char *, const char *);
416
static byte* get_var_key(const byte* rec, uint* len, my_bool t);
unknown's avatar
unknown committed
417
static VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
unknown's avatar
unknown committed
418 419 420
		     int val_len);

static void var_free(void* v);
421

422
void dump_result_to_reject_file(const char *record_file, char *buf, int size);
423
void dump_result_to_log_file(const char *record_file, char *buf, int size);
unknown's avatar
unknown committed
424

425
int close_connection(struct st_query*);
unknown's avatar
unknown committed
426
static void set_charset(struct st_query*);
427 428
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
429 430
int eval_expr(VAR* v, const char *p, const char** p_end);
static int read_server_arguments(const char *name);
431

432
/* Definitions for replace result */
unknown's avatar
unknown committed
433 434 435 436 437

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. */
438
  uint	array_allocs,max_count,length,max_length;
unknown's avatar
unknown committed
439 440 441 442 443
} POINTER_ARRAY;

struct st_replace;
struct st_replace *init_replace(my_string *from, my_string *to, uint count,
				my_string word_end_chars);
444
void free_replace();
unknown's avatar
unknown committed
445
static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name);
446 447
static void replace_strings_append(struct st_replace *rep, DYNAMIC_STRING* ds,
                                   const char *from, int len);
unknown's avatar
unknown committed
448
void free_pointer_array(POINTER_ARRAY *pa);
449 450
static void do_eval(DYNAMIC_STRING *query_eval, const char *query,
                    my_bool pass_through_escape_chars);
451
static void str_to_file(const char *fname, char *str, int size);
452 453

#ifdef __WIN__
unknown's avatar
unknown committed
454
static void free_tmp_sh_file();
455 456
static void free_win_path_patterns();
#endif
unknown's avatar
unknown committed
457 458

struct st_replace *glob_replace;
459
static int eval_result = 0;
unknown's avatar
unknown committed
460

461 462 463 464 465 466 467
/* 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
468
/* Disable functions that only exist in MySQL 4.0 */
unknown's avatar
SCRUM  
unknown committed
469
#if MYSQL_VERSION_ID < 40000
unknown's avatar
unknown committed
470 471 472
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; }
473
my_bool mysql_rpl_probe(MYSQL *mysql __attribute__((unused))) { return 1; }
unknown's avatar
unknown committed
474
#endif
475 476
static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
				      int len);
477
static void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val);
478 479 480 481
static void handle_error(const char *query, struct st_query *q,
			 unsigned int err_errno, const char *err_error,
			 const char *err_sqlstate, DYNAMIC_STRING *ds);
static void handle_no_error(struct st_query *q);
482

483 484
static void do_eval(DYNAMIC_STRING* query_eval, const char *query,
                    my_bool pass_through_escape_chars)
485
{
486
  const char *p;
487
  register char c, next_c;
488 489
  register int escaped = 0;
  VAR* v;
unknown's avatar
unknown committed
490
  DBUG_ENTER("do_eval");
unknown's avatar
unknown committed
491

492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
  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 '\\':
509
      next_c= *(p+1);
510 511 512 513 514
      if (escaped)
      {
	escaped = 0;
	dynstr_append_mem(query_eval, p, 1);
      }
515 516 517
      else if (next_c == '\\' || next_c == '$')
      {
        /* Set escaped only if next char is \ or $ */
518
	escaped = 1;
519 520 521 522 523 524

        if (pass_through_escape_chars)
        {
          /* The escape char should be added to the output string. */
          dynstr_append_mem(query_eval, p, 1);
        }
525 526 527
      }
      else
	dynstr_append_mem(query_eval, p, 1);
528 529 530 531
      break;
    default:
      dynstr_append_mem(query_eval, p, 1);
      break;
532
    }
533
  }
unknown's avatar
unknown committed
534
  DBUG_VOID_RETURN;
535
}
unknown's avatar
unknown committed
536

537

538 539
static void close_cons()
{
540 541 542
  DBUG_ENTER("close_cons");
  for (--next_con; next_con >= cons; --next_con)
  {
543 544 545
    if (next_con->stmt)
      mysql_stmt_close(next_con->stmt);
    next_con->stmt= 0;
546
    mysql_close(&next_con->mysql);
547 548
    if (next_con->util_mysql)
      mysql_close(next_con->util_mysql);
549 550 551 552 553
    my_free(next_con->name, MYF(MY_ALLOW_ZERO_PTR));
  }
  DBUG_VOID_RETURN;
}

554

555 556
static void close_files()
{
unknown's avatar
unknown committed
557
  DBUG_ENTER("close_files");
558
  for (; cur_file >= file_stack; cur_file--)
559
  {
560 561 562 563 564
    DBUG_PRINT("info", ("file_name: %s", cur_file->file_name));
    if (cur_file->file && cur_file->file != stdin)
      my_fclose(cur_file->file, MYF(0));
    my_free((gptr)cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR));
    cur_file->file_name= 0;
unknown's avatar
unknown committed
565 566
  }
  DBUG_VOID_RETURN;
567 568
}

569

570 571 572 573
static void free_used_memory()
{
  uint i;
  DBUG_ENTER("free_used_memory");
574
#ifndef EMBEDDED_LIBRARY
575 576
  if (manager)
    mysql_manager_close(manager);
577
#endif
578 579
  close_cons();
  close_files();
unknown's avatar
unknown committed
580
  hash_free(&var_hash);
unknown's avatar
unknown committed
581

582 583 584
  for (i=0 ; i < q_lines.elements ; i++)
  {
    struct st_query **q= dynamic_element(&q_lines, i, struct st_query**);
585
    my_free((gptr) (*q)->query_buf,MYF(MY_ALLOW_ZERO_PTR));
586 587
    my_free((gptr) (*q),MYF(0));
  }
588
  for (i=0; i < 10; i++)
unknown's avatar
unknown committed
589 590 591 592
  {
    if (var_reg[i].alloced_len)
      my_free(var_reg[i].str_val, MYF(MY_WME));
  }
593 594
  while (embedded_server_arg_count > 1)
    my_free(embedded_server_args[--embedded_server_arg_count],MYF(0));
595 596
  delete_dynamic(&q_lines);
  dynstr_free(&ds_res);
unknown's avatar
unknown committed
597
  free_replace();
598
  free_replace_column();
599 600
  my_free(pass,MYF(MY_ALLOW_ZERO_PTR));
  free_defaults(default_argv);
unknown's avatar
unknown committed
601
  mysql_server_end();
602
  free_re();
603
#ifdef __WIN__
unknown's avatar
unknown committed
604
  free_tmp_sh_file();
605 606
  free_win_path_patterns();
#endif
607
  DBUG_VOID_RETURN;
608 609
}

610
static void die(const char *fmt, ...)
611 612
{
  va_list args;
613
  DBUG_ENTER("die");
614 615

  /* Print the error message */
616
  va_start(args, fmt);
617 618
  if (fmt)
  {
619 620
    fprintf(stderr, "mysqltest: ");
    if (cur_file && cur_file != file_stack)
621
      fprintf(stderr, "In included file \"%s\": ",
622
              cur_file->file_name);
unknown's avatar
unknown committed
623 624
    if (start_lineno != 0)
      fprintf(stderr, "At line %u: ", start_lineno);
625 626
    vfprintf(stderr, fmt, args);
    fprintf(stderr, "\n");
unknown's avatar
unknown committed
627
    fflush(stderr);
628
  }
629
  va_end(args);
630

631
  /* Dump the result that has been accumulated so far to .log file */
632
  if (result_file && ds_res.length)
633
    dump_result_to_log_file(result_file, ds_res.str, ds_res.length);
634 635

  /* Clean up and exit */
636
  free_used_memory();
637
  my_end(MY_CHECK_ERROR);
unknown's avatar
unknown committed
638 639 640 641

  if (!silent)
    printf("not ok\n");

642 643 644
  exit(1);
}

645 646
/* Note that we will get some memory leaks when calling this! */

647 648
static void abort_not_supported_test()
{
649
  DBUG_ENTER("abort_not_supported_test");
650 651 652
  fprintf(stderr, "This test is not supported by this installation\n");
  if (!silent)
    printf("skipped\n");
653
  free_used_memory();
654
  my_end(MY_CHECK_ERROR);
unknown's avatar
unknown committed
655
  exit(62);
656 657
}

658
static void verbose_msg(const char *fmt, ...)
659 660
{
  va_list args;
661 662 663
  DBUG_ENTER("verbose_msg");
  if (!verbose)
    DBUG_VOID_RETURN;
664 665 666

  va_start(args, fmt);

unknown's avatar
unknown committed
667
  fprintf(stderr, "mysqltest: ");
668
  if (start_lineno != 0)
unknown's avatar
unknown committed
669
    fprintf(stderr, "At line %u: ", start_lineno);
670 671 672
  vfprintf(stderr, fmt, args);
  fprintf(stderr, "\n");
  va_end(args);
673
  DBUG_VOID_RETURN;
674 675
}

unknown's avatar
unknown committed
676

677 678
void init_parser()
{
679 680
  parser.current_line= parser.read_lines= 0;
  memset(&var_reg, 0, sizeof(var_reg));
681
}
unknown's avatar
unknown committed
682 683


684
static int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname)
unknown's avatar
unknown committed
685 686
{
  MY_STAT stat_info;
687 688
  char *tmp, *res_ptr;
  char eval_file[FN_REFLEN];
unknown's avatar
unknown committed
689
  int res;
690
  uint res_len;
unknown's avatar
unknown committed
691
  int fd;
692
  DYNAMIC_STRING res_ds;
693 694
  DBUG_ENTER("dyn_string_cmp");

695 696 697 698 699 700 701 702 703
  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)))
704
    die(NullS);
705 706 707 708
  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));
709
    DBUG_PRINT("info",("result: '%s'", ds->str));
710
    DBUG_RETURN(RESULT_LENGTH_MISMATCH);
711
  }
712
  if (!(tmp = (char*) my_malloc(stat_info.st_size + 1, MYF(MY_WME))))
713
    die(NullS);
714 715

  if ((fd = my_open(eval_file, O_RDONLY, MYF(MY_WME))) < 0)
716
    die(NullS);
717
  if (my_read(fd, (byte*)tmp, stat_info.st_size, MYF(MY_WME|MY_NABP)))
718
    die(NullS);
719 720 721 722
  tmp[stat_info.st_size] = 0;
  init_dynamic_string(&res_ds, "", 0, 65536);
  if (eval_result)
  {
723
    do_eval(&res_ds, tmp, FALSE);
724
    res_ptr = res_ds.str;
725
    if ((res_len = res_ds.length) != ds->length)
726
    {
727
      res= RESULT_LENGTH_MISMATCH;
728 729 730 731 732 733 734 735
      goto err;
    }
  }
  else
  {
    res_ptr = tmp;
    res_len = stat_info.st_size;
  }
unknown's avatar
unknown committed
736

737 738
  res= (memcmp(res_ptr, ds->str, res_len)) ?
    RESULT_CONTENT_MISMATCH : RESULT_OK;
unknown's avatar
unknown committed
739

740 741 742 743
err:
  if (res && eval_result)
    str_to_file(fn_format(eval_file, fname, "", ".eval",2), res_ptr,
		res_len);
unknown's avatar
unknown committed
744

745 746
  my_free((gptr) tmp, MYF(0));
  my_close(fd, MYF(MY_WME));
747
  dynstr_free(&res_ds);
unknown's avatar
unknown committed
748

749
  DBUG_RETURN(res);
unknown's avatar
unknown committed
750 751
}

752 753
/*
  Check the content of ds against content of file fname
unknown's avatar
unknown committed
754

755 756 757 758
  SYNOPSIS
  check_result
  ds - content to be checked
  fname - name of file to check against
unknown's avatar
unknown committed
759 760
  require_option - if set and check fails, the test will be aborted
                   with the special exit code "not supported test"
unknown's avatar
unknown committed
761

762 763 764 765 766
  RETURN VALUES
   error - the function will not return

*/
static void check_result(DYNAMIC_STRING* ds, const char *fname,
767
			my_bool require_option)
unknown's avatar
unknown committed
768
{
769
  int res= dyn_string_cmp(ds, fname);
770
  DBUG_ENTER("check_result");
771 772 773

  if (res && require_option)
    abort_not_supported_test();
774
  switch (res) {
775
  case RESULT_OK:
776
    break; /* ok */
777
  case RESULT_LENGTH_MISMATCH:
778 779
    dump_result_to_reject_file(fname, ds->str, ds->length);
    die("Result length mismatch");
780
    break;
781
  case RESULT_CONTENT_MISMATCH:
782 783
    dump_result_to_reject_file(fname, ds->str, ds->length);
    die("Result content mismatch");
784 785 786 787
    break;
  default: /* impossible */
    die("Unknown error code from dyn_string_cmp()");
  }
788 789

  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
790 791
}

792

793
VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw,
794
	     my_bool ignore_not_existing)
795 796 797
{
  int digit;
  VAR* v;
798 799 800 801
  DBUG_ENTER("var_get");
  DBUG_PRINT("enter",("var_name: %s",var_name));

  if (*var_name != '$')
802
    goto err;
803
  digit = *++var_name - '0';
unknown's avatar
unknown committed
804
  if (digit < 0 || digit >= 10)
805
  {
806
    const char *save_var_name = var_name, *end;
807
    uint length;
unknown's avatar
unknown committed
808
    end = (var_name_end) ? *var_name_end : 0;
809
    while (my_isvar(charset_info,*var_name) && var_name != end)
810
      var_name++;
811 812 813 814
    if (var_name == save_var_name)
    {
      if (ignore_not_existing)
	DBUG_RETURN(0);
unknown's avatar
unknown committed
815
      die("Empty variable");
816
    }
817
    length= (uint) (var_name - save_var_name);
818 819
    if (length >= MAX_VAR_NAME)
      die("Too long variable name: %s", save_var_name);
unknown's avatar
unknown committed
820

821
    if (!(v = (VAR*) hash_search(&var_hash, save_var_name, length)))
822
    {
823
      char buff[MAX_VAR_NAME+1];
unknown's avatar
unknown committed
824
      strmake(buff, save_var_name, length);
825
      v= var_from_env(buff, "");
826
    }
827
    var_name--;					/* Point at last character */
828
  }
829
  else
830
    v = var_reg + digit;
unknown's avatar
unknown committed
831

832 833 834 835
  if (!raw && v->int_dirty)
  {
    sprintf(v->str_val, "%d", v->int_val);
    v->int_dirty = 0;
836
    v->str_val_len = strlen(v->str_val);
837
  }
838
  if (var_name_end)
839
    *var_name_end = var_name  ;
840
  DBUG_RETURN(v);
841 842
err:
  if (var_name_end)
843 844
    *var_name_end = 0;
  die("Unsupported variable name: %s", var_name);
845
  DBUG_RETURN(0);
846 847
}

848
static VAR *var_obtain(const char *name, int len)
unknown's avatar
unknown committed
849 850
{
  VAR* v;
851
  if ((v = (VAR*)hash_search(&var_hash, name, len)))
unknown's avatar
unknown committed
852
    return v;
853
  v = var_init(0, name, len, "", 0);
unknown's avatar
SCRUM  
unknown committed
854
  my_hash_insert(&var_hash, (byte*)v);
unknown's avatar
unknown committed
855 856 857
  return v;
}

858 859
int var_set(const char *var_name, const char *var_name_end,
            const char *var_val, const char *var_val_end)
860 861 862
{
  int digit;
  VAR* v;
863 864 865 866 867 868
  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)));

869
  if (*var_name++ != '$')
870 871 872 873
  {
    var_name--;
    die("Variable name in %s does not start with '$'", var_name);
  }
874
  digit = *var_name - '0';
875
  if (!(digit < 10 && digit >= 0))
876 877 878
  {
    v = var_obtain(var_name, (uint) (var_name_end - var_name));
  }
879
  else
880
    v = var_reg + digit;
881
  DBUG_RETURN(eval_expr(v, var_val, (const char**)&var_val_end));
882 883
}

884

885
int open_file(const char *name)
unknown's avatar
unknown committed
886
{
887
  char buff[FN_REFLEN];
888 889
  DBUG_ENTER("open_file");
  DBUG_PRINT("enter", ("name: %s", name));
890 891 892 893 894 895 896
  if (!test_if_hard_path(name))
  {
    strxmov(buff, opt_basedir, name, NullS);
    name=buff;
  }
  fn_format(buff,name,"","",4);

897
  if (cur_file == file_stack_end)
unknown's avatar
unknown committed
898
    die("Source directives are nesting too deep");
899
  cur_file++;
900 901 902
  if (!(cur_file->file = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0))))
  {
    cur_file--;
903
    die("Could not open file %s", buff);
904 905
  }
  cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
906
  cur_file->lineno=1;
907
  DBUG_RETURN(0);
908
}
909

910 911 912 913 914 915 916 917

/*
  Check for unexpected "junk" after the end of query
  This is normally caused by missing delimiters
*/

int check_eol_junk(const char *eol)
{
918
  const char *p= eol;
919 920
  DBUG_ENTER("check_eol_junk");
  DBUG_PRINT("enter", ("eol: %s", eol));
921
  /* Remove all spacing chars except new line */
922
  while (*p && my_isspace(charset_info, *p) && (*p != '\n'))
923 924 925
    p++;

  /* Check for extra delimiter */
926
  if (*p && !strncmp(p, delimiter, delimiter_length))
927
    die("Extra delimiter \"%s\" found", delimiter);
928

929 930 931 932 933 934 935
  /* Allow trailing # comment */
  if (*p && *p != '#')
  {
    if (*p == '\n')
      die("Missing delimiter");
    die("End of line junk detected: \"%s\"", p);
  }
936
  DBUG_RETURN(0);
unknown's avatar
unknown committed
937 938
}

unknown's avatar
unknown committed
939

940
/* ugly long name, but we are following the convention */
941
int do_wait_for_slave_to_stop(struct st_query *q __attribute__((unused)))
942 943 944 945
{
  MYSQL* mysql = &cur_con->mysql;
  for (;;)
  {
unknown's avatar
unknown committed
946
    MYSQL_RES *res;
947 948 949
    MYSQL_ROW row;
    int done;
    LINT_INIT(res);
950

unknown's avatar
unknown committed
951 952
    if (mysql_query(mysql,"show status like 'Slave_running'") ||
	!(res=mysql_store_result(mysql)))
953 954 955 956 957 958 959 960 961 962 963
      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
964
    my_sleep(SLAVE_POLL_INTERVAL);
965 966 967 968
  }
  return 0;
}

969
int do_require_manager(struct st_query *query __attribute__((unused)) )
970 971 972 973 974 975
{
  if (!manager)
    abort_not_supported_test();
  return 0;
}

976
#ifndef EMBEDDED_LIBRARY
977
static int do_server_op(struct st_query *q, const char *op)
978
{
979 980
  char *p= q->first_argument;
  char com_buf[256], *com_p;
981 982 983 984
  if (!manager)
  {
    die("Manager is not initialized, manager commands are not possible");
  }
985 986
  com_p= strmov(com_buf,op);
  com_p= strmov(com_p,"_exec ");
987
  if (!*p)
988 989
    die("Missing server name in server_%s", op);
  while (*p && !my_isspace(charset_info, *p))
unknown's avatar
unknown committed
990
   *com_p++= *p++;
991 992 993 994 995 996
  *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);
997 998
  while (!manager->eof)
  {
999
    if (mysql_manager_fetch_line(manager, com_buf, sizeof(com_buf)))
1000 1001 1002 1003
      die("Error fetching result line: %s(%d)", manager->last_error,
	  manager->last_errno);
  }

1004
  q->last_argument= p;
1005 1006
  return 0;
}
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017

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");
}

1018
#endif
1019

1020 1021 1022 1023 1024 1025

/*
  Source and execute the given file

  SYNOPSIS
    do_source()
1026
    query	called command
1027 1028 1029 1030 1031 1032 1033 1034

  DESCRIPTION
    source <file_name>

    Open the file <file_name> and execute it

*/

1035
int do_source(struct st_query *query)
unknown's avatar
unknown committed
1036
{
1037
  char *p= query->first_argument, *name;
1038
  if (!*p)
1039
    die("Missing file name in source");
1040
  name= p;
1041
  while (*p && !my_isspace(charset_info,*p))
unknown's avatar
unknown committed
1042
    p++;
1043 1044
  if (*p)
    *p++= 0;
1045 1046
  query->last_argument= p;
  /*
unknown's avatar
unknown committed
1047 1048
     If this file has already been sourced, don't source it again.
     It's already available in the q_lines cache.
1049
  */
1050 1051
  if (parser.current_line < (parser.read_lines - 1))
    return 0;
unknown's avatar
unknown committed
1052 1053 1054
  return open_file(name);
}

1055
#ifdef __WIN__
unknown's avatar
unknown committed
1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
/* Variables used for temuprary sh files used for emulating Unix on Windows */
char tmp_sh_name[64], tmp_sh_cmd[70];

static void init_tmp_sh_file()
{
  /* Format a name for the tmp sh file that is unique for this process */
  my_snprintf(tmp_sh_name, sizeof(tmp_sh_name), "tmp_%d.sh", getpid());
  /* Format the command to execute in order to run the script */
  my_snprintf(tmp_sh_cmd, sizeof(tmp_sh_cmd), "sh %s", tmp_sh_name);
}

static void free_tmp_sh_file()
{
  my_delete(tmp_sh_name, MYF(0));
}
1071
#endif
unknown's avatar
unknown committed
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083

FILE* my_popen(DYNAMIC_STRING* ds_cmd, const char* mode)
{
#ifdef __WIN__
  /* Dump the command into a sh script file and execute with popen */
  str_to_file(tmp_sh_name, ds_cmd->str, ds_cmd->length);
  return popen(tmp_sh_cmd, mode);
#else
  return popen(ds_cmd->str, mode);
#endif
}

1084

1085 1086 1087 1088 1089
/*
  Execute given command.

  SYNOPSIS
    do_exec()
1090
    query	called command
1091 1092

  DESCRIPTION
1093 1094 1095 1096 1097 1098
    exec <command>

    Execute the text between exec and end of line in a subprocess.
    The error code returned from the subprocess is checked against the
    expected error array, previously set with the --error command.
    It can thus be used to execute a command that shall fail.
1099

1100 1101 1102 1103
  NOTE
     Although mysqltest is executed from cygwin shell, the command will be
     executed in "cmd.exe". Thus commands like "rm" etc can NOT be used, use
     system for those commands.
1104 1105
*/

1106
static void do_exec(struct st_query *query)
1107
{
unknown's avatar
unknown committed
1108
  int error;
1109 1110
  char buf[1024];
  FILE *res_file;
1111
  char *cmd= query->first_argument;
1112
  DYNAMIC_STRING ds_cmd;
unknown's avatar
unknown committed
1113
  DBUG_ENTER("do_exec");
1114
  DBUG_PRINT("enter", ("cmd: '%s'", cmd));
1115

unknown's avatar
unknown committed
1116
  while (*cmd && my_isspace(charset_info, *cmd))
1117 1118
    cmd++;
  if (!*cmd)
1119
    die("Missing argument in exec");
1120
  query->last_argument= query->end;
1121

1122 1123
  init_dynamic_string(&ds_cmd, 0, strlen(cmd)+256, 256);
  /* Eval the command, thus replacing all environment variables */
1124
  do_eval(&ds_cmd, cmd, TRUE);
1125 1126
  cmd= ds_cmd.str;

unknown's avatar
unknown committed
1127
  DBUG_PRINT("info", ("Executing '%s' as '%s'",
1128
                      query->first_argument, cmd));
unknown's avatar
unknown committed
1129

unknown's avatar
unknown committed
1130
  if (!(res_file= my_popen(&ds_cmd, "r")) && query->abort_on_error)
1131
    die("popen(\"%s\", \"r\") failed", query->first_argument);
unknown's avatar
unknown committed
1132

1133
  while (fgets(buf, sizeof(buf), res_file))
1134
  {
1135
    if (disable_result_log)
1136 1137 1138 1139
    {
      buf[strlen(buf)-1]=0;
      DBUG_PRINT("exec_result",("%s", buf));
    }
1140 1141 1142 1143
    else
    {
      replace_dynstr_append(&ds_res, buf);
    }
unknown's avatar
unknown committed
1144 1145 1146
  }
  error= pclose(res_file);
  if (error != 0)
unknown's avatar
patch  
unknown committed
1147
  {
unknown's avatar
unknown committed
1148 1149 1150
    uint status= WEXITSTATUS(error), i;
    my_bool ok= 0;

1151
    if (query->abort_on_error)
1152
      die("command \"%s\" failed", query->first_argument);
unknown's avatar
unknown committed
1153 1154 1155

    DBUG_PRINT("info",
               ("error: %d, status: %d", error, status));
1156
    for (i= 0; i < query->expected_errors; i++)
unknown's avatar
patch  
unknown committed
1157
    {
unknown's avatar
unknown committed
1158
      DBUG_PRINT("info", ("expected error: %d",
1159 1160 1161
                          query->expected_errno[i].code.errnum));
      if ((query->expected_errno[i].type == ERR_ERRNO) &&
          (query->expected_errno[i].code.errnum == status))
1162
      {
unknown's avatar
unknown committed
1163
        ok= 1;
1164
        DBUG_PRINT("info", ("command \"%s\" failed with expected error: %d",
1165
                            query->first_argument, status));
1166
      }
unknown's avatar
patch  
unknown committed
1167
    }
unknown's avatar
unknown committed
1168
    if (!ok)
1169
      die("command \"%s\" failed with wrong error: %d",
1170
          query->first_argument, status);
unknown's avatar
patch  
unknown committed
1171
  }
1172 1173
  else if (query->expected_errno[0].type == ERR_ERRNO &&
           query->expected_errno[0].code.errnum != 0)
unknown's avatar
patch  
unknown committed
1174 1175
  {
    /* Error code we wanted was != 0, i.e. not an expected success */
1176
    die("command \"%s\" succeeded - should have failed with errno %d...",
1177
        query->first_argument, query->expected_errno[0].code.errnum);
unknown's avatar
patch  
unknown committed
1178
  }
unknown's avatar
unknown committed
1179

1180
  free_replace();
1181
  DBUG_VOID_RETURN;
1182 1183
}

1184 1185 1186 1187 1188 1189 1190 1191
/*
  Set variable from the result of a query

  SYNOPSIS
    var_query_set()
    var	        variable to set from query
    query       start of query string to execute
    query_end   end of the query string to execute
unknown's avatar
unknown committed
1192

1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203

  DESCRIPTION
    let @<var_name> = `<query>`

    Execute the query and assign the first row of result to var as
    a tab separated strings

    Also assign each column of the result set to
    variable "$<var_name>_<column_name>"
    Thus the tab separated output can be read from $<var_name> and
    and each individual column can be read as $<var_name>_<col_name>
unknown's avatar
unknown committed
1204

1205 1206 1207
*/

int var_query_set(VAR* var, const char *query, const char** query_end)
1208
{
1209 1210
  char* end = (char*)((query_end && *query_end) ?
		      *query_end : query + strlen(query));
1211 1212 1213 1214
  MYSQL_RES *res;
  MYSQL_ROW row;
  MYSQL* mysql = &cur_con->mysql;
  LINT_INIT(res);
unknown's avatar
unknown committed
1215

1216
  while (end > query && *end != '`')
1217
    --end;
1218
  if (query == end)
1219
    die("Syntax error in query, missing '`'");
1220
  ++query;
1221

1222
  if (mysql_real_query(mysql, query, (int)(end - query)) ||
1223 1224 1225
      !(res = mysql_store_result(mysql)))
  {
    *end = 0;
1226 1227
    die("Error running query '%s': %d: %s", query,
	mysql_errno(mysql) ,mysql_error(mysql));
1228 1229 1230
  }

  if ((row = mysql_fetch_row(res)) && row[0])
1231 1232 1233 1234 1235 1236 1237 1238 1239
  {
    /*
      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;
1240
    MYSQL_FIELD *fields= mysql_fetch_fields(res);
1241 1242 1243 1244 1245 1246

    init_dynamic_string(&result, "", 16384, 65536);
    lengths= mysql_fetch_lengths(res);
    for (i=0; i < mysql_num_fields(res); i++)
    {
      if (row[0])
1247
      {
1248
#ifdef NOT_YET
1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261
	/* Add to <var_name>_<col_name> */
	uint j;
	char var_col_name[MAX_VAR_NAME];
	uint length= snprintf(var_col_name, MAX_VAR_NAME,
			      "$%s_%s", var->name, fields[i].name);
	/* Convert characters not allowed in variable names to '_' */
	for (j= 1; j < length; j++)
	{
	  if (!my_isvar(charset_info,var_col_name[j]))
	     var_col_name[j]= '_';
        }
	var_set(var_col_name,  var_col_name + length,
		row[i], row[i] + lengths[i]);
1262
#endif
1263
        /* Add column to tab separated string */
1264
	dynstr_append_mem(&result, row[i], lengths[i]);
1265
      }
1266 1267 1268
      dynstr_append_mem(&result, "\t", 1);
    }
    end= result.str + result.length-1;
1269
    eval_expr(var, result.str, (const char**) &end);
1270 1271
    dynstr_free(&result);
  }
1272
  else
1273
    eval_expr(var, "", 0);
1274 1275 1276 1277

  mysql_free_result(res);
  return 0;
}
1278

1279
void var_copy(VAR *dest, VAR *src)
unknown's avatar
unknown committed
1280
{
1281 1282 1283 1284
  dest->int_val= src->int_val;
  dest->int_dirty= src->int_dirty;

  /* Alloc/realloc data for str_val in dest */
unknown's avatar
unknown committed
1285
  if (dest->alloced_len < src->alloced_len &&
1286 1287 1288
      !(dest->str_val= dest->str_val
        ? my_realloc(dest->str_val, src->alloced_len, MYF(MY_WME))
        : my_malloc(src->alloced_len, MYF(MY_WME))))
unknown's avatar
unknown committed
1289
    die("Out of memory");
1290 1291 1292 1293 1294 1295 1296
  else
    dest->alloced_len= src->alloced_len;

  /* Copy str_val data to dest */
  dest->str_val_len= src->str_val_len;
  if (src->str_val_len)
    memcpy(dest->str_val, src->str_val, src->str_val_len);
unknown's avatar
unknown committed
1297 1298
}

1299
int eval_expr(VAR* v, const char *p, const char** p_end)
1300 1301
{
  VAR* vp;
1302
  if (*p == '$')
1303 1304
  {
    if ((vp = var_get(p,p_end,0,0)))
1305
    {
1306 1307
      var_copy(v, vp);
      return 0;
unknown's avatar
unknown committed
1308
    }
1309
  }
1310
  else if (*p == '`')
1311 1312 1313
  {
    return var_query_set(v, p, p_end);
  }
unknown's avatar
unknown committed
1314 1315
  else
    {
1316 1317 1318 1319
      int new_val_len = (p_end && *p_end) ?
	 (int) (*p_end - p) : (int) strlen(p);
      if (new_val_len + 1 >= v->alloced_len)
      {
1320
	v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
1321 1322
	  MIN_VAR_ALLOC : new_val_len + 1;
	if (!(v->str_val =
1323
	      v->str_val ? my_realloc(v->str_val, v->alloced_len+1,
1324
				      MYF(MY_WME)) :
1325
	      my_malloc(v->alloced_len+1, MYF(MY_WME))))
1326 1327 1328 1329 1330
	  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
1331 1332
      v->int_val=atoi(p);
      v->int_dirty=0;
1333 1334
      return 0;
    }
1335

1336 1337 1338 1339
  die("Invalid expr: %s", p);
  return 1;
}

1340

1341
enum enum_operator
1342
{
1343 1344 1345
  DO_DEC,
  DO_INC
};
1346

1347
/*
1348
  Decrease or increase the value of a variable
1349 1350

  SYNOPSIS
1351 1352 1353
    do_modify_var()
    query	called command
    operator    operation to perform on the var
1354 1355 1356

  DESCRIPTION
    dec $var_name
1357
    inc $var_name
1358

1359 1360
*/

unknown's avatar
unknown committed
1361
int do_modify_var(struct st_query *query,
1362
                  enum enum_operator operator)
1363
{
1364
  const char *p= query->first_argument;
1365
  VAR* v;
1366
  if (!*p)
1367
    die("Missing argument to %.*s", query->first_word_len, query->query);
1368
  if (*p != '$')
1369
    die("The argument to %.*s must be a variable (start with $)",
unknown's avatar
unknown committed
1370
        query->first_word_len, query->query);
1371
  v= var_get(p, &p, 1, 0);
1372
  switch (operator) {
1373 1374 1375 1376 1377 1378 1379
  case DO_DEC:
    v->int_val--;
    break;
  case DO_INC:
    v->int_val++;
    break;
  default:
1380
    die("Invalid operator to do_modify_var");
1381 1382 1383
    break;
  }
  v->int_dirty= 1;
1384
  query->last_argument= (char*)++p;
1385 1386 1387
  return 0;
}

1388

unknown's avatar
unknown committed
1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402
/*
  Wrapper for 'system' function

  NOTE
   If mysqltest is executed from cygwin shell, the command will be
   executed in the "windows command interpreter" cmd.exe and we prepend "sh"
   to make it be executed by cygwins "bash". Thus commands like "rm",
   "mkdir" as well as shellscripts can executed by "system" in Windows.

*/

int my_system(DYNAMIC_STRING* ds_cmd)
{
#ifdef __WIN__
unknown's avatar
unknown committed
1403
  /* Dump the command into a sh script file and execute with system */
unknown's avatar
unknown committed
1404
  str_to_file(tmp_sh_name, ds_cmd->str, ds_cmd->length);
unknown's avatar
unknown committed
1405
  return system(tmp_sh_cmd);
unknown's avatar
unknown committed
1406 1407 1408 1409 1410 1411
#else
  return system(ds_cmd->str);
#endif
}


1412 1413 1414 1415 1416 1417 1418 1419 1420 1421
/*

  SYNOPSIS
  do_system
    command	called command

  DESCRIPTION
    system <command>

    Eval the query to expand any $variables in the command.
unknown's avatar
unknown committed
1422
    Execute the command with the "system" command.
1423

unknown's avatar
unknown committed
1424
*/
1425

unknown's avatar
unknown committed
1426
void do_system(struct st_query *command)
unknown's avatar
unknown committed
1427
{
1428
  DYNAMIC_STRING ds_cmd;
unknown's avatar
unknown committed
1429
  DBUG_ENTER("do_system");
1430

1431 1432 1433 1434
  if (strlen(command->first_argument) == 0)
    die("Missing arguments to system, nothing to do!");

  init_dynamic_string(&ds_cmd, 0, strlen(command->first_argument) + 64, 256);
1435

1436
  /* Eval the system command, thus replacing all environment variables */
1437
  do_eval(&ds_cmd, command->first_argument, TRUE);
1438 1439 1440

  DBUG_PRINT("info", ("running system command '%s' as '%s'",
                      command->first_argument, ds_cmd.str));
unknown's avatar
unknown committed
1441
  if (my_system(&ds_cmd))
unknown's avatar
unknown committed
1442
  {
1443 1444
    if (command->abort_on_error)
      die("system command '%s' failed", command->first_argument);
1445

1446 1447 1448 1449
    /* If ! abort_on_error, log message and continue */
    dynstr_append(&ds_res, "system command '");
    replace_dynstr_append(&ds_res, command->first_argument);
    dynstr_append(&ds_res, "' failed\n");
unknown's avatar
unknown committed
1450
  }
1451 1452

  command->last_argument= command->end;
unknown's avatar
unknown committed
1453
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1454
}
1455

1456 1457 1458

/*
  Print the content between echo and <delimiter> to result file.
1459 1460
  Evaluate all variables in the string before printing, allow
  for variable names to be escaped using \
1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472

  SYNOPSIS
    do_echo()
    q  called command

  DESCRIPTION
    echo text
    Print the text after echo until end of command to result file

    echo $<var_name>
    Print the content of the variable <var_name> to result file

1473 1474 1475 1476 1477 1478
    echo Some text $<var_name>
    Print "Some text" plus the content of the variable <var_name> to
    result file

    echo Some text \$<var_name>
    Print "Some text" plus $<var_name> to result file
1479 1480
*/

1481
int do_echo(struct st_query *command)
1482
{
1483
  DYNAMIC_STRING *ds, ds_echo;
1484

1485
  ds= &ds_res;
1486

1487
  init_dynamic_string(&ds_echo, "", 256, 256);
unknown's avatar
unknown committed
1488
  do_eval(&ds_echo, command->first_argument, FALSE);
1489
  dynstr_append_mem(ds, ds_echo.str, ds_echo.length);
1490
  dynstr_append_mem(ds, "\n", 1);
1491 1492
  dynstr_free(&ds_echo);
  command->last_argument= command->end;
1493 1494 1495
  return 0;
}

1496

1497
int do_sync_with_master2(long offset)
unknown's avatar
unknown committed
1498 1499 1500
{
  MYSQL_RES* res;
  MYSQL_ROW row;
1501
  MYSQL* mysql= &cur_con->mysql;
unknown's avatar
unknown committed
1502
  char query_buf[FN_REFLEN+128];
1503
  int tries= 0;
1504 1505
  int rpl_parse;

1506
  if (!master_pos.file[0])
unknown's avatar
unknown committed
1507
    die("Calling 'sync_with_master' without calling 'save_master_pos'");
1508
  rpl_parse= mysql_rpl_parse_enabled(mysql);
1509
  mysql_disable_rpl_parse(mysql);
unknown's avatar
unknown committed
1510

unknown's avatar
unknown committed
1511
  sprintf(query_buf, "select master_pos_wait('%s', %ld)", master_pos.file,
1512
	  master_pos.pos + offset);
1513 1514 1515

wait_for_position:

1516
  if (mysql_query(mysql, query_buf))
unknown's avatar
unknown committed
1517 1518
    die("failed in %s: %d: %s", query_buf, mysql_errno(mysql),
        mysql_error(mysql));
unknown's avatar
unknown committed
1519

1520
  if (!(res= mysql_store_result(mysql)))
unknown's avatar
unknown committed
1521
    die("mysql_store_result() returned NULL for '%s'", query_buf);
1522
  if (!(row= mysql_fetch_row(res)))
unknown's avatar
unknown committed
1523
    die("empty result in %s", query_buf);
1524
  if (!row[0])
1525 1526 1527 1528 1529 1530
  {
    /*
      It may be that the slave SQL thread has not started yet, though START
      SLAVE has been issued ?
    */
    if (tries++ == 3)
unknown's avatar
unknown committed
1531
      die("could not sync with master ('%s' returned NULL)", query_buf);
1532 1533 1534 1535
    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
1536
  mysql_free_result(res);
1537
  if (rpl_parse)
1538
    mysql_enable_rpl_parse(mysql);
unknown's avatar
unknown committed
1539

unknown's avatar
unknown committed
1540 1541 1542
  return 0;
}

1543
int do_sync_with_master(struct st_query *query)
1544
{
1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557
  long offset= 0;
  char *p= query->first_argument;
  const char *offset_start= p;
  if (*offset_start)
  {
    for (; my_isdigit(charset_info, *p); p++)
      offset = offset * 10 + *p - '0';

    if(*p && !my_isspace(charset_info, *p))
      die("Invalid integer argument \"%s\"", offset_start);
    query->last_argument= p;
  }
  return do_sync_with_master2(offset);
1558
}
1559

unknown's avatar
unknown committed
1560 1561 1562 1563 1564
int do_save_master_pos()
{
  MYSQL_RES* res;
  MYSQL_ROW row;
  MYSQL* mysql = &cur_con->mysql;
1565
  const char *query;
1566 1567 1568 1569
  int rpl_parse;

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

1571
  if (mysql_query(mysql, query= "show master status"))
1572
    die("failed in show master status: %d: %s",
unknown's avatar
unknown committed
1573 1574
	mysql_errno(mysql), mysql_error(mysql));

1575
  if (!(res = mysql_store_result(mysql)))
unknown's avatar
unknown committed
1576
    die("mysql_store_result() retuned NULL for '%s'", query);
1577
  if (!(row = mysql_fetch_row(res)))
unknown's avatar
unknown committed
1578
    die("empty result in show master status");
1579 1580
  strnmov(master_pos.file, row[0], sizeof(master_pos.file)-1);
  master_pos.pos = strtoul(row[1], (char**) 0, 10);
1581
  mysql_free_result(res);
unknown's avatar
unknown committed
1582

1583
  if (rpl_parse)
1584
    mysql_enable_rpl_parse(mysql);
unknown's avatar
unknown committed
1585

unknown's avatar
unknown committed
1586 1587 1588 1589
  return 0;
}


1590 1591 1592 1593 1594
/*
  Assign the variable <var_name> with <var_val>

  SYNOPSIS
   do_let()
1595
    query	called command
1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608

  DESCRIPTION
    let $<var_name>=<var_val><delimiter>

    <var_name>  - is the string string found between the $ and =
    <var_val>   - is the content between the = and <delimiter>, it may span
                  multiple line and contain any characters except <delimiter>
    <delimiter> - is a string containing of one or more chars, default is ;

  RETURN VALUES
   Program will die if error detected
*/

1609
int do_let(struct st_query *query)
1610
{
1611
  char *p= query->first_argument;
1612
  char *var_name, *var_name_end, *var_val_start;
1613 1614

  /* Find <var_name> */
1615
  if (!*p)
1616
    die("Missing arguments to let");
1617 1618
  var_name= p;
  while (*p && (*p != '=') && !my_isspace(charset_info,*p))
1619
    p++;
1620 1621
  var_name_end= p;
  if (var_name+1 == var_name_end)
1622
    die("Missing variable name in let");
1623
  while (my_isspace(charset_info,*p))
1624
    p++;
1625
  if (*p++ != '=')
1626 1627 1628
    die("Missing assignment operator in let");

  /* Find start of <var_val> */
1629
  while (*p && my_isspace(charset_info,*p))
1630
    p++;
1631 1632
  var_val_start= p;
  query->last_argument= query->end;
1633
  /* Assign var_val to var_name */
1634
  return var_set(var_name, var_name_end, var_val_start, query->end);
1635 1636
}

1637 1638 1639 1640 1641 1642 1643 1644

/*
  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)
1645
{
1646 1647 1648 1649
  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);
1650 1651
}

1652

1653
int do_rpl_probe(struct st_query *query __attribute__((unused)))
1654
{
unknown's avatar
unknown committed
1655
  DBUG_ENTER("do_rpl_probe");
1656
  if (mysql_rpl_probe(&cur_con->mysql))
unknown's avatar
unknown committed
1657 1658
    die("Failed in mysql_rpl_probe(): '%s'", mysql_error(&cur_con->mysql));
  DBUG_RETURN(0);
1659 1660
}

1661

1662
int do_enable_rpl_parse(struct st_query *query __attribute__((unused)))
1663 1664 1665 1666 1667
{
  mysql_enable_rpl_parse(&cur_con->mysql);
  return 0;
}

1668

1669
int do_disable_rpl_parse(struct st_query *query __attribute__((unused)))
1670 1671 1672 1673 1674 1675
{
  mysql_disable_rpl_parse(&cur_con->mysql);
  return 0;
}


1676 1677 1678 1679 1680 1681 1682
/*
  Sleep the number of specifed seconds

  SYNOPSIS
   do_sleep()
    q	       called command
    real_sleep  use the value from opt_sleep as number of seconds to sleep
unknown's avatar
unknown committed
1683
                if real_sleep is false
1684 1685 1686

  DESCRIPTION
    sleep <seconds>
unknown's avatar
unknown committed
1687 1688 1689 1690 1691 1692 1693 1694 1695
    real_sleep <seconds>

  The difference between the sleep and real_sleep commands is that sleep
  uses the delay from the --sleep command-line option if there is one.
  (If the --sleep option is not given, the sleep command uses the delay
  specified by its argument.) The real_sleep command always uses the
  delay specified by its 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.
1696 1697
*/

1698
int do_sleep(struct st_query *query, my_bool real_sleep)
unknown's avatar
unknown committed
1699
{
1700 1701 1702 1703 1704 1705
  int error= 0;
  char *p= query->first_argument;
  char *sleep_start, *sleep_end= query->end;
  double sleep_val;

  while (my_isspace(charset_info, *p))
unknown's avatar
unknown committed
1706
    p++;
1707
  if (!*p)
unknown's avatar
unknown committed
1708
    die("Missing argument to %.*s", query->first_word_len, query->query);
1709 1710 1711
  sleep_start= p;
  /* Check that arg starts with a digit, not handled by my_strtod */
  if (!my_isdigit(charset_info, *sleep_start))
unknown's avatar
unknown committed
1712 1713
    die("Invalid argument to %.*s \"%s\"", query->first_word_len, query->query,
		query->first_argument);
1714 1715
  sleep_val= my_strtod(sleep_start, &sleep_end, &error);
  if (error)
unknown's avatar
unknown committed
1716 1717
    die("Invalid argument to %.*s \"%s\"", query->first_word_len, query->query,
		query->first_argument);
1718 1719

  /* Fixed sleep time selected by --sleep option */
1720
  if (opt_sleep && !real_sleep)
1721 1722
    sleep_val= opt_sleep;

1723
  DBUG_PRINT("info", ("sleep_val: %f", sleep_val));
1724 1725
  my_sleep((ulong) (sleep_val * 1000000L));
  query->last_argument= sleep_end;
unknown's avatar
unknown committed
1726
  return 0;
unknown's avatar
unknown committed
1727 1728
}

1729
static void get_file_name(char *filename, struct st_query *q)
1730
{
1731
  char *p= q->first_argument, *name;
1732 1733
  if (!*p)
    die("Missing file name argument");
1734
  name= p;
1735 1736 1737 1738
  while (*p && !my_isspace(charset_info,*p))
    p++;
  if (*p)
    *p++= 0;
1739
  q->last_argument= p;
1740
  strmake(filename, name, FN_REFLEN);
1741 1742
}

1743
static void set_charset(struct st_query *q)
unknown's avatar
unknown committed
1744
{
1745 1746
  char *charset_name= q->first_argument;
  char *p;
unknown's avatar
unknown committed
1747 1748

  if (!charset_name || !*charset_name)
1749
    die("Missing charset name in 'character_set'");
unknown's avatar
unknown committed
1750
  /* Remove end space */
1751 1752 1753 1754 1755
  p= charset_name;
  while (*p && !my_isspace(charset_info,*p))
    p++;
  if(*p)
    *p++= 0;
1756
  q->last_argument= p;
unknown's avatar
unknown committed
1757 1758 1759 1760
  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
1761

1762
static uint get_errcodes(match_err *to,struct st_query *q)
unknown's avatar
unknown committed
1763
{
1764
  char *p= q->first_argument;
1765
  uint count= 0;
1766

1767
  DBUG_ENTER("get_errcodes");
1768

unknown's avatar
unknown committed
1769
  if (!*p)
1770
    die("Missing argument in %s", q->query);
1771

1772
  do
1773
  {
1774 1775 1776
    if (*p == 'S')
    {
      /* SQLSTATE string */
1777 1778 1779 1780 1781 1782 1783
      char *end= ++p + SQLSTATE_LENGTH;
      char *to_ptr= to[count].code.sqlstate;

      for (; my_isalnum(charset_info, *p) && p != end; p++)
	*to_ptr++= *p;
      *to_ptr= 0;

1784 1785
      to[count].type= ERR_SQLSTATE;
    }
1786 1787 1788 1789 1790
    else if (*p == 'E')
    {
      /* SQL error as string */
      st_error *e= global_error;
      char *start= p++;
unknown's avatar
unknown committed
1791

1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803
      for (; *p == '_' || my_isalnum(charset_info, *p); p++)
	;
      for (; e->name; e++)
      {
	if (!strncmp(start, e->name, (int) (p - start)))
	{
	  to[count].code.errnum= (uint) e->code;
	  to[count].type= ERR_ERRNO;
	  break;
	}
      }
      if (!e->name)
1804
	die("Unknown SQL error '%s'", start);
1805
    }
1806 1807 1808
    else
    {
      long val;
1809 1810

      if (!(p= str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val)))
1811
	die("Invalid argument in %s", q->query);
1812 1813 1814
      to[count].code.errnum= (uint) val;
      to[count].type= ERR_ERRNO;
    }
unknown's avatar
unknown committed
1815
    count++;
1816
  } while (*(p++) == ',');
1817
  q->last_argument= (p - 1);
1818
  to[count].type= ERR_EMPTY;                        /* End of data */
unknown's avatar
unknown committed
1819
  DBUG_RETURN(count);
unknown's avatar
unknown committed
1820 1821
}

unknown's avatar
unknown committed
1822 1823 1824
/*
  Get a string;  Return ptr to end of string
  Strings may be surrounded by " or '
1825 1826

  If string is a '$variable', return the value of the variable.
unknown's avatar
unknown committed
1827 1828 1829
*/


1830
static char *get_string(char **to_ptr, char **from_ptr,
1831
			struct st_query *q)
unknown's avatar
unknown committed
1832 1833
{
  reg1 char c,sep;
1834
  char *to= *to_ptr, *from= *from_ptr, *start=to;
unknown's avatar
unknown committed
1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878
  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)
1879
    die("Wrong string argument in %s", q->query);
unknown's avatar
unknown committed
1880

1881
  while (my_isspace(charset_info,*from))	/* Point to next string */
unknown's avatar
unknown committed
1882 1883
    from++;

1884 1885
  *to =0;				/* End of string marker */
  *to_ptr= to+1;			/* Store pointer to end */
unknown's avatar
unknown committed
1886
  *from_ptr= from;
1887 1888 1889 1890 1891 1892 1893 1894

  /* 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)
    {
1895
      DBUG_PRINT("info",("var: '%s' -> '%s'", start, var->str_val));
1896 1897 1898 1899
      DBUG_RETURN(var->str_val);	/* return found variable value */
    }
  }
  DBUG_RETURN(start);
unknown's avatar
unknown committed
1900 1901 1902 1903 1904 1905 1906
}


/*
  Get arguments for replace. The syntax is:
  replace from to [from to ...]
  Where each argument may be quoted with ' or "
1907 1908
  A argument may also be a variable, in which case the value of the
  variable is replaced.
unknown's avatar
unknown committed
1909 1910 1911 1912 1913
*/

static void get_replace(struct st_query *q)
{
  uint i;
1914
  char *from= q->first_argument;
1915
  char *buff,*start;
unknown's avatar
unknown committed
1916 1917 1918 1919
  char word_end_chars[256],*pos;
  POINTER_ARRAY to_array,from_array;
  DBUG_ENTER("get_replace");

unknown's avatar
unknown committed
1920
  free_replace();
1921

unknown's avatar
unknown committed
1922 1923 1924
  bzero((char*) &to_array,sizeof(to_array));
  bzero((char*) &from_array,sizeof(from_array));
  if (!*from)
1925
    die("Missing argument in %s", q->query);
1926
  start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
unknown's avatar
unknown committed
1927 1928 1929
  while (*from)
  {
    char *to=buff;
1930
    to=get_string(&buff, &from, q);
unknown's avatar
unknown committed
1931
    if (!*from)
1932
      die("Wrong number of arguments to replace_result in '%s'", q->query);
unknown's avatar
unknown committed
1933
    insert_pointer_name(&from_array,to);
1934
    to=get_string(&buff, &from, q);
unknown's avatar
unknown committed
1935 1936 1937
    insert_pointer_name(&to_array,to);
  }
  for (i=1,pos=word_end_chars ; i < 256 ; i++)
1938
    if (my_isspace(charset_info,i))
unknown's avatar
unknown committed
1939
      *pos++= i;
1940
  *pos=0;					/* End pointer */
unknown's avatar
unknown committed
1941 1942 1943
  if (!(glob_replace=init_replace((char**) from_array.typelib.type_names,
				  (char**) to_array.typelib.type_names,
				  (uint) from_array.typelib.count,
1944
				  word_end_chars)))
1945
    die("Can't initialize replace from '%s'", q->query);
unknown's avatar
unknown committed
1946 1947
  free_pointer_array(&from_array);
  free_pointer_array(&to_array);
1948
  my_free(start, MYF(0));
1949
  q->last_argument= q->end;
1950
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1951 1952 1953 1954
}

void free_replace()
{
1955
  DBUG_ENTER("free_replace");
unknown's avatar
unknown committed
1956 1957 1958 1959 1960
  if (glob_replace)
  {
    my_free((char*) glob_replace,MYF(0));
    glob_replace=0;
  }
1961
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1962 1963
}

unknown's avatar
unknown committed
1964
struct connection * find_connection_by_name(const char *name)
unknown's avatar
unknown committed
1965 1966
{
  struct connection *con;
1967
  for (con= cons; con < next_con; con++)
1968
  {
1969
    if (!strcmp(con->name, name))
1970
    {
unknown's avatar
unknown committed
1971
      return con;
1972 1973
    }
  }
unknown's avatar
unknown committed
1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985
  return 0; /* Connection not found */
}


int select_connection_name(const char *name)
{
  DBUG_ENTER("select_connection2");
  DBUG_PRINT("enter",("name: '%s'", name));

  if (!(cur_con= find_connection_by_name(name)))
    die("connection '%s' not found in connection pool", name);
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1986 1987
}

1988 1989

int select_connection(struct st_query *query)
1990
{
1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007
  char *name;
  char *p= query->first_argument;
  DBUG_ENTER("select_connection");

  if (!*p)
    die("Missing connection name in connect");
  name= p;
  while (*p && !my_isspace(charset_info,*p))
    p++;
  if (*p)
    *p++= 0;
  query->last_argument= p;
  return select_connection_name(name);
}


int close_connection(struct st_query *q)
2008
{
2009
  char *p= q->first_argument, *name;
2010
  struct connection *con;
2011 2012 2013
  DBUG_ENTER("close_connection");
  DBUG_PRINT("enter",("name: '%s'",p));

2014
  if (!*p)
unknown's avatar
unknown committed
2015
    die("Missing connection name in disconnect");
2016
  name= p;
2017
  while (*p && !my_isspace(charset_info,*p))
2018
    p++;
2019

2020 2021
  if (*p)
    *p++= 0;
2022 2023
  q->last_argument= p;
  for (con= cons; con < next_con; con++)
2024
  {
2025
    if (!strcmp(con->name, name))
2026
    {
2027
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
2028 2029 2030
      if (q->type == Q_DIRTY_CLOSE)
      {
	if (con->mysql.net.vio)
unknown's avatar
unknown committed
2031
	{
unknown's avatar
unknown committed
2032 2033
	  vio_delete(con->mysql.net.vio);
	  con->mysql.net.vio = 0;
unknown's avatar
unknown committed
2034
	}
unknown's avatar
unknown committed
2035
      }
2036
#endif
2037
      mysql_close(&con->mysql);
2038 2039 2040
      if (con->util_mysql)
	mysql_close(con->util_mysql);
      con->util_mysql= 0;
unknown's avatar
unknown committed
2041 2042 2043 2044 2045 2046 2047 2048
      my_free(con->name, MYF(0));
      /*
         When the connection is closed set name to "closed_connection"
         to make it possible to reuse the connection name.
         The connection slot will not be reused
       */
      if (!(con->name = my_strdup("closed_connection", MYF(MY_WME))))
        die("Out of memory");
2049 2050 2051
      DBUG_RETURN(0);
    }
  }
2052
  die("connection '%s' not found in connection pool", name);
2053
  DBUG_RETURN(1);				/* Never reached */
2054 2055
}

unknown's avatar
unknown committed
2056

2057 2058
/*
   This one now is a hack - we may want to improve in in the
unknown's avatar
unknown committed
2059 2060 2061
   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
unknown's avatar
unknown committed
2062 2063 2064 2065

  SYNOPSIS
  safe_get_param
  str - string to get param from
unknown's avatar
unknown committed
2066
  arg - pointer to string where result will be stored
unknown's avatar
unknown committed
2067 2068
  msg - Message to display if param is not found
       if msg is 0 this param is not required and param may be empty
unknown's avatar
unknown committed
2069

unknown's avatar
unknown committed
2070 2071
  RETURNS
  pointer to str after param
unknown's avatar
unknown committed
2072

2073
*/
2074

unknown's avatar
unknown committed
2075
char* safe_get_param(char *str, char** arg, const char *msg)
unknown's avatar
unknown committed
2076
{
2077
  DBUG_ENTER("safe_get_param");
unknown's avatar
unknown committed
2078 2079
  if(!*str)
  {
unknown's avatar
unknown committed
2080
    if (msg)
unknown's avatar
unknown committed
2081 2082 2083 2084
      die(msg);
    *arg= str;
    DBUG_RETURN(str);
  }
2085
  while (*str && my_isspace(charset_info,*str))
unknown's avatar
unknown committed
2086
    str++;
2087
  *arg= str;
unknown's avatar
unknown committed
2088 2089
  while (*str && *str != ',' && *str != ')')
    str++;
unknown's avatar
unknown committed
2090
  if (msg && !*arg)
unknown's avatar
unknown committed
2091
    die(msg);
2092

2093
  *str++= 0;
2094
  DBUG_RETURN(str);
unknown's avatar
unknown committed
2095 2096
}

2097
#ifndef EMBEDDED_LIBRARY
2098 2099 2100 2101 2102 2103 2104 2105 2106 2107
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);

}
2108
#endif
2109

2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120

/*
  Connect to a server doing several retries if needed.

  SYNOPSIS
    safe_connect()
      con               - connection structure to be used
      host, user, pass, - connection parameters
      db, port, sock

  NOTE
2121 2122 2123 2124 2125 2126 2127 2128 2129 2130

    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

    This function will try to connect to the given server
    "opt_max_connect_retries" times and sleep "connection_retry_sleep"
    seconds between attempts before finally giving up.
    This helps in situation when the client starts
2131 2132 2133 2134 2135 2136 2137 2138 2139
    before the server (which happens sometimes).
    It will ignore any errors during these retries. One should use
    connect_n_handle_errors() if he expects a connection error and wants
    handle as if it was an error from a usual statement.

  RETURN VALUE
    0 - success, non-0 - failure
*/

2140 2141
int safe_connect(MYSQL* mysql, const char *host, const char *user,
		 const char *pass, const char *db, int port, const char *sock)
unknown's avatar
unknown committed
2142
{
2143 2144
  int con_error= 1;
  my_bool reconnect= 1;
2145
  static int connection_retry_sleep= 2; /* Seconds */
unknown's avatar
unknown committed
2146
  int i;
2147
  for (i= 0; i < opt_max_connect_retries; i++)
unknown's avatar
unknown committed
2148
  {
2149
    if (mysql_real_connect(mysql, host,user, pass, db, port, sock,
2150
			   CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS))
unknown's avatar
unknown committed
2151
    {
2152
      con_error= 0;
unknown's avatar
unknown committed
2153 2154
      break;
    }
2155
    sleep(connection_retry_sleep);
unknown's avatar
unknown committed
2156
  }
2157 2158 2159 2160
  /*
   TODO: change this to 0 in future versions, but the 'kill' test relies on
   existing behavior
  */
2161
  mysql_options(mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
unknown's avatar
unknown committed
2162 2163 2164
  return con_error;
}

2165

2166
/*
unknown's avatar
unknown committed
2167
  Connect to a server and handle connection errors in case they occur.
2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193

  SYNOPSIS
    connect_n_handle_errors()
      q                 - context of connect "query" (command)
      con               - connection structure to be used
      host, user, pass, - connection parameters
      db, port, sock
      create_conn       - out parameter, set to zero if connection was
                          not established and is not touched otherwise

  DESCRIPTION
    This function will try to establish a connection to server and handle
    possible errors in the same manner as if "connect" was usual SQL-statement
    (If error is expected it will ignore it once it occurs and log the
    "statement" to the query log).
    Unlike safe_connect() it won't do several attempts.

  RETURN VALUE
    0 - success, non-0 - failure
*/

int connect_n_handle_errors(struct st_query *q, MYSQL* con, const char* host,
                            const char* user, const char* pass,
                            const char* db, int port, const char* sock,
                            int* create_conn)
{
2194
  DYNAMIC_STRING *ds;
2195
  my_bool reconnect= 1;
2196 2197
  int error= 0;

2198
  ds= &ds_res;
2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230

  if (!disable_query_log)
  {
    /*
      It is nice to have connect() statement logged in result file
      in this case.
      QQ: Should we do this only if we are expecting an error ?
    */
    char port_buff[22]; /* This should be enough for any int */
    char *port_end;
    dynstr_append_mem(ds, "connect(", 8);
    replace_dynstr_append(ds, host);
    dynstr_append_mem(ds, ",", 1);
    replace_dynstr_append(ds, user);
    dynstr_append_mem(ds, ",", 1);
    replace_dynstr_append(ds, pass);
    dynstr_append_mem(ds, ",", 1);
    if (db)
      replace_dynstr_append(ds, db);
    dynstr_append_mem(ds, ",", 1);
    port_end= int10_to_str(port, port_buff, 10);
    replace_dynstr_append_mem(ds, port_buff, port_end - port_buff);
    dynstr_append_mem(ds, ",", 1);
    if (sock)
      replace_dynstr_append(ds, sock);
    dynstr_append_mem(ds, ")", 1);
    dynstr_append_mem(ds, delimiter, delimiter_length);
    dynstr_append_mem(ds, "\n", 1);
  }
  if (!mysql_real_connect(con, host, user, pass, db, port, sock ? sock: 0,
                          CLIENT_MULTI_STATEMENTS))
  {
2231 2232
    handle_error("connect", q, mysql_errno(con), mysql_error(con),
		 mysql_sqlstate(con), ds);
2233 2234 2235
    *create_conn= 0;
    goto err;
  }
2236

2237
  handle_no_error(q);
2238

2239 2240 2241 2242
  /*
   TODO: change this to 0 in future versions, but the 'kill' test relies on
   existing behavior
  */
2243
  mysql_options(con, MYSQL_OPT_RECONNECT, (char *)&reconnect);
2244

2245 2246 2247 2248 2249 2250
err:
  free_replace();
  return error;
}


unknown's avatar
unknown committed
2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274
/*
  Open a new connection to MySQL Server with the parameters
  specified

  SYNOPSIS
   do_connect()
    q	       called command

  DESCRIPTION
    connect(<name>,<host>,<user>,<pass>,<db>,[<port>,<sock>[<opts>]]);

      <name> - name of the new connection
      <host> - hostname of server
      <user> - user to connect as
      <pass> - password used when connecting
      <db>   - initial db when connected
      <port> - server port
      <sock> - server socket
      <opts> - options to use for the connection
               SSL - use SSL if available
               COMPRESS - use compression if available

 */

2275
int do_connect(struct st_query *q)
unknown's avatar
unknown committed
2276
{
2277
  char *con_name, *con_user,*con_pass, *con_host, *con_port_str,
unknown's avatar
unknown committed
2278 2279
    *con_db, *con_sock, *con_options;
  char *con_buf, *p;
2280
  char buff[FN_REFLEN];
unknown's avatar
unknown committed
2281
  int con_port;
unknown's avatar
unknown committed
2282 2283
  bool con_ssl= 0;
  bool con_compress= 0;
2284
  int free_con_sock= 0;
2285 2286
  int error= 0;
  int create_conn= 1;
2287
  VAR *var_port, *var_sock;
unknown's avatar
unknown committed
2288

2289
  DBUG_ENTER("do_connect");
2290
  DBUG_PRINT("enter",("connect: %s", q->first_argument));
unknown's avatar
unknown committed
2291

unknown's avatar
unknown committed
2292 2293 2294 2295 2296
  /* Make a copy of query before parsing, safe_get_param will modify */
  if (!(con_buf= my_strdup(q->first_argument, MYF(MY_WME))))
    die("Could not allocate con_buf");
  p= con_buf;

2297
  if (*p != '(')
2298
    die("Syntax error in connect - expected '(' found '%c'", *p);
unknown's avatar
unknown committed
2299
  p++;
unknown's avatar
unknown committed
2300 2301 2302 2303 2304
  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
2305 2306

  /* Port */
unknown's avatar
unknown committed
2307
  p= safe_get_param(p, &con_port_str, 0);
unknown's avatar
unknown committed
2308
  if (*con_port_str)
unknown's avatar
unknown committed
2309
  {
2310 2311
    if (*con_port_str == '$')
    {
2312
      if (!(var_port= var_get(con_port_str, 0, 0, 0)))
unknown's avatar
unknown committed
2313
        die("Unknown variable '%s'", con_port_str+1);
2314
      con_port= var_port->int_val;
2315 2316
    }
    else
unknown's avatar
unknown committed
2317
    {
2318
      con_port= atoi(con_port_str);
unknown's avatar
unknown committed
2319 2320 2321 2322 2323 2324 2325 2326 2327 2328
      if (con_port == 0)
        die("Illegal argument for port: '%s'", con_port_str);
    }
  }
  else
  {
    con_port= port;
  }

  /* Sock */
unknown's avatar
unknown committed
2329
  p= safe_get_param(p, &con_sock, 0);
unknown's avatar
unknown committed
2330 2331
  if (*con_sock)
  {
2332 2333
    if (*con_sock == '$')
    {
2334
      if (!(var_sock= var_get(con_sock, 0, 0, 0)))
unknown's avatar
unknown committed
2335
        die("Unknown variable '%s'", con_sock+1);
2336
      if (!(con_sock= (char*)my_malloc(var_sock->str_val_len+1, MYF(0))))
unknown's avatar
unknown committed
2337
        die("Out of memory");
2338
      free_con_sock= 1;
2339
      memcpy(con_sock, var_sock->str_val, var_sock->str_val_len);
2340
      con_sock[var_sock->str_val_len]= 0;
2341
    }
unknown's avatar
unknown committed
2342
  }
unknown's avatar
unknown committed
2343 2344 2345 2346 2347 2348
  else
  {
    con_sock= (char*) unix_sock;
  }

  /* Options */
unknown's avatar
unknown committed
2349
  p= safe_get_param(p, &con_options, 0);
unknown's avatar
unknown committed
2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363
  while (*con_options)
  {
    char* str= con_options;
    while (*str && !my_isspace(charset_info, *str))
      str++;
    *str++= 0;
    if (!strcmp(con_options, "SSL"))
      con_ssl= 1;
    else if (!strcmp(con_options, "COMPRESS"))
      con_compress= 1;
    else
      die("Illegal option to connect: %s", con_options);
    con_options= str;
  }
2364 2365
  /* Note: 'p' is pointing into the copy 'con_buf' */
  q->last_argument= q->first_argument + (p - con_buf);
unknown's avatar
unknown committed
2366

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

unknown's avatar
unknown committed
2370 2371 2372
  if (find_connection_by_name(con_name))
    die("Connection %s already exists", con_name);

2373
  if (!mysql_init(&next_con->mysql))
unknown's avatar
unknown committed
2374
    die("Failed on mysql_init()");
unknown's avatar
unknown committed
2375
  if (opt_compress || con_compress)
2376
    mysql_options(&next_con->mysql, MYSQL_OPT_COMPRESS, NullS);
2377
  mysql_options(&next_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
unknown's avatar
unknown committed
2378
  mysql_options(&next_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name);
2379

unknown's avatar
unknown committed
2380
#ifdef HAVE_OPENSSL
unknown's avatar
unknown committed
2381
  if (opt_use_ssl || con_ssl)
2382
  {
unknown's avatar
unknown committed
2383 2384
    mysql_ssl_set(&next_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
		  opt_ssl_capath, opt_ssl_cipher);
2385 2386 2387
    mysql_options(&next_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
                  &opt_ssl_verify_server_cert);
  }
unknown's avatar
unknown committed
2388
#endif
2389
  if (con_sock && !free_con_sock && *con_sock && *con_sock != FN_LIBCHAR)
2390
    con_sock=fn_format(buff, con_sock, TMPDIR, "",0);
unknown's avatar
unknown committed
2391
  if (!con_db[0])
2392
    con_db= db;
2393
  /* Special database to allow one to connect without a database name */
unknown's avatar
unknown committed
2394
  if (con_db && !strcmp(con_db,"*NO-ONE*"))
2395
    con_db= 0;
2396 2397
  if (q->abort_on_error)
  {
2398 2399 2400 2401
    if (safe_connect(&next_con->mysql, con_host, con_user, con_pass,
		     con_db, con_port, con_sock ? con_sock: 0))
      die("Could not open connection '%s': %d %s", con_name,
          mysql_errno(&next_con->mysql), mysql_error(&next_con->mysql));
2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413
  }
  else
    error= connect_n_handle_errors(q, &next_con->mysql, con_host, con_user,
                                   con_pass, con_db, con_port, con_sock,
                                   &create_conn);

  if (create_conn)
  {
    if (!(next_con->name= my_strdup(con_name, MYF(MY_WME))))
      die(NullS);
    cur_con= next_con++;
  }
2414 2415
  if (free_con_sock)
    my_free(con_sock, MYF(MY_WME));
unknown's avatar
unknown committed
2416
  my_free(con_buf, MYF(MY_WME));
2417
  DBUG_RETURN(error);
unknown's avatar
unknown committed
2418 2419
}

2420

2421
int do_done(struct st_query *q)
2422
{
unknown's avatar
unknown committed
2423
  /* Check if empty block stack */
2424
  if (cur_block == block_stack)
2425 2426 2427
  {
    if (*q->query != '}')
      die("Stray 'end' command - end of block before beginning");
2428
    die("Stray '}' - end of block before beginning");
2429
  }
unknown's avatar
unknown committed
2430 2431 2432

  /* Test if inner block has been executed */
  if (cur_block->ok && cur_block->cmd == cmd_while)
2433
  {
unknown's avatar
unknown committed
2434 2435 2436
    /* Pop block from stack, re-execute outer block */
    cur_block--;
    parser.current_line = cur_block->line;
2437
  }
2438
  else
unknown's avatar
unknown committed
2439
  {
unknown's avatar
unknown committed
2440 2441 2442
    /* Pop block from stack, goto next line */
    cur_block--;
    parser.current_line++;
unknown's avatar
unknown committed
2443
  }
2444 2445 2446
  return 0;
}

unknown's avatar
unknown committed
2447

2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473
/*
  Process start of a "if" or "while" statement

  SYNOPSIS
   do_block()
    cmd        Type of block
    q	       called command

  DESCRIPTION
    if ([!]<expr>)
    {
      <block statements>
    }

    while ([!]<expr>)
    {
      <block statements>
    }

    Evaluates the <expr> and if it evaluates to
    greater than zero executes the following code block.
    A '!' can be used before the <expr> to indicate it should
    be executed if it evaluates to zero.

 */

2474
void do_block(enum block_cmd cmd, struct st_query* q)
2475
{
2476 2477
  char *p= q->first_argument;
  const char *expr_start, *expr_end;
2478
  VAR v;
2479
  const char *cmd_name= (cmd == cmd_while ? "while" : "if");
2480 2481 2482
  my_bool not_expr= FALSE;
  DBUG_ENTER("do_block");
  DBUG_PRINT("enter", ("%s", cmd_name));
unknown's avatar
unknown committed
2483 2484

  /* Check stack overflow */
2485
  if (cur_block == block_stack_end)
2486
    die("Nesting too deeply");
unknown's avatar
unknown committed
2487 2488 2489 2490 2491 2492

  /* Set way to find outer block again, increase line counter */
  cur_block->line= parser.current_line++;

  /* If this block is ignored */
  if (!cur_block->ok)
2493
  {
unknown's avatar
unknown committed
2494 2495 2496 2497
    /* Inner block should be ignored too */
    cur_block++;
    cur_block->cmd= cmd;
    cur_block->ok= FALSE;
2498
    DBUG_VOID_RETURN;
2499
  }
unknown's avatar
unknown committed
2500

unknown's avatar
unknown committed
2501
  /* Parse and evaluate test expression */
2502
  expr_start= strchr(p, '(');
2503
  if (!expr_start++)
2504
    die("missing '(' in %s", cmd_name);
2505 2506 2507 2508 2509 2510 2511 2512

  /* Check for !<expr> */
  if (*expr_start == '!')
  {
    not_expr= TRUE;
    expr_start++; /* Step past the '!' */
  }
  /* Find ending ')' */
2513
  expr_end= strrchr(expr_start, ')');
2514
  if (!expr_end)
2515
    die("missing ')' in %s", cmd_name);
2516 2517 2518 2519
  p= (char*)expr_end+1;

  while (*p && my_isspace(charset_info, *p))
    p++;
2520 2521
  if (*p == '{')
    die("Missing newline between %s and '{'", cmd_name);
2522
  if (*p)
2523
    die("Missing '{' after %s. Found \"%s\"", cmd_name, p);
2524

unknown's avatar
unknown committed
2525
  var_init(&v,0,0,0,0);
2526
  eval_expr(&v, expr_start, &expr_end);
unknown's avatar
unknown committed
2527 2528 2529 2530 2531 2532

  /* Define inner block */
  cur_block++;
  cur_block->cmd= cmd;
  cur_block->ok= (v.int_val ? TRUE : FALSE);

2533 2534 2535 2536 2537
  if (not_expr)
    cur_block->ok = !cur_block->ok;

  DBUG_PRINT("info", ("OK: %d", cur_block->ok));

unknown's avatar
unknown committed
2538
  var_free(&v);
2539
  DBUG_VOID_RETURN;
2540 2541
}

unknown's avatar
unknown committed
2542

2543 2544 2545 2546 2547 2548 2549 2550 2551 2552
/*
  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.
*/

2553
int my_getc(FILE *file)
2554 2555 2556
{
  if (line_buffer_pos == line_buffer)
    return fgetc(file);
2557
  return *--line_buffer_pos;
2558 2559 2560 2561
}

void my_ungetc(int c)
{
2562
  *line_buffer_pos++= (char) c;
2563 2564 2565 2566
}


my_bool end_of_query(int c)
2567
{
2568
  uint i;
2569
  char tmp[MAX_DELIMITER];
2570

2571 2572 2573 2574
  if (c != *delimiter)
    return 0;

  for (i= 1; i < delimiter_length &&
2575
	 (c= my_getc(cur_file->file)) == *(delimiter + i);
2576
       i++)
2577 2578 2579
    tmp[i]= c;

  if (i == delimiter_length)
2580 2581 2582
    return 1;					/* Found delimiter */

  /* didn't find delimiter, push back things that we read */
2583 2584 2585
  my_ungetc(c);
  while (i > 1)
    my_ungetc(tmp[--i]);
2586 2587 2588 2589
  return 0;
}


2590 2591 2592 2593 2594 2595 2596 2597 2598
/*
  Read one "line" from the file

  SYNOPSIS
    read_line
    buf     buffer for the read line
    size    size of the buffer i.e max size to read

  DESCRIPTION
unknown's avatar
unknown committed
2599 2600
    This function actually reads several lines and adds them to the
    buffer buf. It continues to read until it finds what it believes
2601 2602 2603 2604 2605
    is a complete query.

    Normally that means it will read lines until it reaches the
    "delimiter" that marks end of query. Default delimiter is ';'
    The function should be smart enough not to detect delimiter's
unknown's avatar
unknown committed
2606
    found inside strings surrounded with '"' and '\'' escaped strings.
2607 2608 2609 2610 2611 2612 2613

    If the first line in a query starts with '#' or '-' this line is treated
    as a comment. A comment is always terminated when end of line '\n' is
    reached.

*/

2614
int read_line(char *buf, int size)
unknown's avatar
unknown committed
2615 2616
{
  int c;
2617
  char quote;
2618
  char *p= buf, *buf_end= buf + size - 1;
2619
  int no_save= 0;
2620 2621
  enum {R_NORMAL, R_Q, R_Q_IN_Q, R_SLASH_IN_Q,
	R_COMMENT, R_LINE_START} state= R_LINE_START;
unknown's avatar
unknown committed
2622
  DBUG_ENTER("read_line");
2623
  LINT_INIT(quote);
2624

2625
  start_lineno= cur_file->lineno;
2626 2627
  for (; p < buf_end ;)
  {
2628
    no_save= 0;
2629 2630
    c= my_getc(cur_file->file);
    if (feof(cur_file->file))
unknown's avatar
unknown committed
2631
    {
unknown's avatar
unknown committed
2632
  found_eof:
unknown's avatar
unknown committed
2633 2634
      if (cur_file->file != stdin)
      {
2635
	my_fclose(cur_file->file, MYF(0));
2636 2637
        cur_file->file= 0;
      }
2638 2639
      my_free((gptr)cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR));
      cur_file->file_name= 0;
2640
      if (cur_file == file_stack)
2641
      {
2642 2643 2644 2645 2646
        /* We're back at the first file, check if
           all { have matching }
         */
        if (cur_block != block_stack)
          die("Missing end of block");
2647

2648
        DBUG_PRINT("info", ("end of file"));
unknown's avatar
unknown committed
2649
	DBUG_RETURN(1);
2650
      }
2651
      cur_file--;
2652
      start_lineno= cur_file->lineno;
unknown's avatar
unknown committed
2653
      continue;
2654
    }
2655

2656
    if (c == '\n')
2657 2658
    {
      /* Line counting is independent of state */
2659
      cur_file->lineno++;
2660

2661 2662 2663 2664 2665
      /* Convert cr/lf to lf */
      if (p != buf && *(p-1) == '\r')
        *(p-1)= 0;
    }

2666 2667
    switch(state) {
    case R_NORMAL:
2668
      /*  Only accept '{' in the beginning of a line */
2669 2670 2671
      if (end_of_query(c))
      {
	*p= 0;
unknown's avatar
unknown committed
2672
	DBUG_RETURN(0);
2673
      }
2674 2675 2676 2677 2678
      else if (c == '\'' || c == '"' || c == '`')
      {
        quote= c;
	state= R_Q;
      }
2679
      else if (c == '\n')
2680
      {
2681
	state = R_LINE_START;
2682
      }
2683 2684 2685 2686
      break;
    case R_COMMENT:
      if (c == '\n')
      {
2687
	*p= 0;
unknown's avatar
unknown committed
2688
	DBUG_RETURN(0);
2689 2690 2691
      }
      break;
    case R_LINE_START:
2692
      /* Only accept start of comment if this is the first line in query */
2693 2694
      if ((cur_file->lineno == start_lineno) &&
	  (c == '#' || c == '-' || parsing_disabled))
2695 2696 2697
      {
	state = R_COMMENT;
      }
2698
      else if (my_isspace(charset_info, c))
2699 2700
      {
	if (c == '\n')
2701
	  start_lineno= cur_file->lineno; /* Query hasn't started yet */
2702
	no_save= 1;
2703
      }
2704 2705
      else if (c == '}')
      {
2706 2707
	*buf++= '}';
	*buf= 0;
unknown's avatar
unknown committed
2708
	DBUG_RETURN(0);
2709
      }
2710
      else if (end_of_query(c) || c == '{')
2711
      {
2712
	*p= 0;
unknown's avatar
unknown committed
2713
	DBUG_RETURN(0);
2714
      }
2715
      else if (c == '\'' || c == '"' || c == '`')
2716
      {
2717 2718
        quote= c;
	state= R_Q;
2719
      }
2720
      else
2721
	state= R_NORMAL;
2722
      break;
unknown's avatar
unknown committed
2723

2724 2725 2726
    case R_Q:
      if (c == quote)
	state= R_Q_IN_Q;
2727
      else if (c == '\\')
2728
	state= R_SLASH_IN_Q;
2729
      break;
2730
    case R_Q_IN_Q:
2731 2732 2733
      if (end_of_query(c))
      {
	*p= 0;
unknown's avatar
unknown committed
2734
	DBUG_RETURN(0);
2735
      }
2736
      if (c != quote)
2737
	state= R_NORMAL;
2738
      else
2739
	state= R_Q;
2740
      break;
2741 2742
    case R_SLASH_IN_Q:
      state= R_Q;
2743
      break;
2744

unknown's avatar
unknown committed
2745
    }
2746 2747

    if (!no_save)
unknown's avatar
unknown committed
2748 2749 2750 2751 2752
    {
      /* 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
2753 2754
      /* We give up if multibyte character is started but not */
      /* completed before we pass buf_end */
unknown's avatar
unknown committed
2755 2756 2757 2758 2759 2760 2761 2762 2763
      if ((charlen > 1) && (p + charlen) <= buf_end)
      {
	int i;
	char* mb_start = p;

	*p++ = c;

	for (i= 1; i < charlen; i++)
	{
2764
	  if (feof(cur_file->file))
unknown's avatar
unknown committed
2765
	    goto found_eof;	/* FIXME: could we just break here?! */
2766
	  c= my_getc(cur_file->file);
unknown's avatar
unknown committed
2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780
	  *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;
    }
2781
  }
2782
  *p= 0;					/* Always end with \0 */
2783
  DBUG_RETURN(feof(cur_file->file));
unknown's avatar
unknown committed
2784 2785
}

2786 2787 2788 2789
/*
  Create a query from a set of lines

  SYNOPSIS
2790
    read_query()
2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803
    q_ptr pointer where to return the new query

  DESCRIPTION
    Converts lines returned by read_line into a query, this involves
    parsing the first word in the read line to find the query type.


    A -- comment may contain a valid query as the first word after the
    comment start. Thus it's always checked to see if that is the case.
    The advantage with this approach is to be able to execute commands
    terminated by new line '\n' regardless how many "delimiter" it contain.

*/
unknown's avatar
unknown committed
2804

2805 2806
static char read_query_buf[MAX_QUERY];

2807
int read_query(struct st_query** q_ptr)
unknown's avatar
unknown committed
2808
{
2809
  char *p= read_query_buf;
2810
  struct st_query* q;
unknown's avatar
unknown committed
2811
  DBUG_ENTER("read_query");
2812

2813 2814
  if (parser.current_line < parser.read_lines)
  {
2815
    get_dynamic(&q_lines, (gptr) q_ptr, parser.current_line) ;
unknown's avatar
unknown committed
2816
    DBUG_RETURN(0);
2817
  }
2818
  if (!(*q_ptr= q= (struct st_query*) my_malloc(sizeof(*q), MYF(MY_WME))) ||
unknown's avatar
unknown committed
2819 2820
      insert_dynamic(&q_lines, (gptr) &q))
    die(NullS);
2821

2822 2823 2824
  q->record_file[0]= 0;
  q->require_file= 0;
  q->first_word_len= 0;
unknown's avatar
unknown committed
2825

2826
  q->type= Q_UNKNOWN;
2827
  q->query_buf= q->query= 0;
2828
  read_query_buf[0]= 0;
2829
  if (read_line(read_query_buf, sizeof(read_query_buf)))
unknown's avatar
unknown committed
2830
  {
2831
    check_eol_junk(read_query_buf);
unknown's avatar
unknown committed
2832
    DBUG_RETURN(1);
unknown's avatar
unknown committed
2833
  }
2834
  
2835
  DBUG_PRINT("info", ("query: %s", read_query_buf));
2836 2837
  if (*p == '#')
  {
2838
    q->type= Q_COMMENT;
unknown's avatar
unknown committed
2839 2840
    /* This goto is to avoid losing the "expected error" info. */
    goto end;
2841
  }
2842 2843 2844 2845 2846 2847 2848 2849
  if (!parsing_disabled)
  {
    memcpy((gptr) q->expected_errno, (gptr) global_expected_errno,
           sizeof(global_expected_errno));
    q->expected_errors= global_expected_errors;
    q->abort_on_error= (global_expected_errors == 0 && abort_on_error);
  }

unknown's avatar
unknown committed
2850
  if (p[0] == '-' && p[1] == '-')
2851
  {
2852 2853
    q->type= Q_COMMENT_WITH_COMMAND;
    p+= 2;					/* To calculate first word */
2854
  }
2855
  else if (!parsing_disabled)
2856
  {
2857
    while (*p && my_isspace(charset_info, *p))
unknown's avatar
unknown committed
2858
      p++ ;
2859
  }
unknown's avatar
unknown committed
2860 2861

end:
2862
  while (*p && my_isspace(charset_info, *p))
unknown's avatar
unknown committed
2863
    p++;
unknown's avatar
unknown committed
2864

2865
  if (!(q->query_buf= q->query= my_strdup(p, MYF(MY_WME))))
2866 2867 2868
    die(NullS);

  /* Calculate first word and first argument */
2869 2870 2871
  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
2872
    p++;
2873 2874
  q->first_argument= p;
  q->end= strend(q->query);
2875
  parser.read_lines++;
unknown's avatar
unknown committed
2876
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2877 2878
}

2879 2880 2881

static struct my_option my_long_options[] =
{
unknown's avatar
unknown committed
2882 2883
  {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
   0, 0, 0, 0, 0, 0},
2884
  {"basedir", 'b', "Basedir for tests.", (gptr*) &opt_basedir,
2885
   (gptr*) &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2886
  {"big-test", 'B', "Define BIG_TEST to 1.", (gptr*) &opt_big_test,
2887
   (gptr*) &opt_big_test, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
2888
  {"compress", 'C', "Use the compressed server/client protocol.",
2889 2890
   (gptr*) &opt_compress, (gptr*) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
   0, 0, 0},
2891 2892 2893 2894
  {"max-connect-retries", OPT_MAX_CONNECT_RETRIES,
   "Max number of connection attempts when connecting to server",
   (gptr*) &opt_max_connect_retries, (gptr*) &opt_max_connect_retries, 0,
   GET_INT, REQUIRED_ARG, 5, 1, 10, 0, 0, 0},
unknown's avatar
unknown committed
2895
  {"cursor-protocol", OPT_CURSOR_PROTOCOL, "Use cursors for prepared statements.",
unknown's avatar
unknown committed
2896 2897
   (gptr*) &cursor_protocol, (gptr*) &cursor_protocol, 0,
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
2898 2899 2900 2901 2902 2903 2904 2905 2906
  {"database", 'D', "Database to use.", (gptr*) &db, (gptr*) &db, 0,
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#ifdef DBUG_OFF
  {"debug", '#', "This is a non-debug version. Catch this and exit",
   0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
#else
  {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
#endif
2907 2908
  {"host", 'h', "Connect to host.", (gptr*) &host, (gptr*) &host, 0,
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2909
  {"manager-host", OPT_MANAGER_HOST, "Undocumented: Used for debugging.",
2910 2911
   (gptr*) &manager_host, (gptr*) &manager_host, 0, GET_STR, REQUIRED_ARG,
   0, 0, 0, 0, 0, 0},
2912
  {"manager-password", OPT_MANAGER_PASSWD, "Undocumented: Used for debugging.",
2913
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2914
  {"manager-port", OPT_MANAGER_PORT, "Undocumented: Used for debugging.",
2915 2916
   (gptr*) &manager_port, (gptr*) &manager_port, 0, GET_INT, REQUIRED_ARG,
   MYSQL_MANAGER_PORT, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
2917 2918 2919
  {"manager-user", OPT_MANAGER_USER, "Undocumented: Used for debugging.",
   (gptr*) &manager_user, (gptr*) &manager_user, 0, GET_STR, REQUIRED_ARG, 0,
   0, 0, 0, 0, 0},
2920
  {"manager-wait-timeout", OPT_MANAGER_WAIT_TIMEOUT,
2921
   "Undocumented: Used for debugging.", (gptr*) &manager_wait_timeout,
2922 2923 2924 2925
   (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,
2926
   (gptr*) &port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2927 2928 2929
  {"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},
2930 2931 2932 2933 2934 2935 2936
  {"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},
unknown's avatar
unknown committed
2937
  {"server-arg", 'A', "Send option value to embedded server as a parameter.",
2938
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2939
  {"server-file", 'F', "Read embedded server arguments from file.",
2940 2941 2942
   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},
2943
  {"skip-safemalloc", OPT_SKIP_SAFEMALLOC,
2944
   "Don't use the memory allocation checking.", 0, 0, 0, GET_NO_ARG, NO_ARG,
2945
   0, 0, 0, 0, 0, 0},
2946
  {"sleep", 'T', "Sleep always this many seconds on sleep commands.",
2947 2948 2949 2950 2951
   (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
2952 2953 2954
  {"sp-protocol", OPT_SP_PROTOCOL, "Use stored procedures for select",
   (gptr*) &sp_protocol, (gptr*) &sp_protocol, 0,
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
2955
#include "sslopt-longopts.h"
2956 2957
  {"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
2958 2959
  {"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},
2960
  {"tmpdir", 't', "Temporary directory where sockets are put.",
2961 2962 2963 2964 2965 2966 2967
   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},
unknown's avatar
unknown committed
2968 2969 2970
  {"view-protocol", OPT_VIEW_PROTOCOL, "Use views for select",
   (gptr*) &view_protocol, (gptr*) &view_protocol, 0,
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
2971
  { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
unknown's avatar
unknown committed
2972 2973
};

unknown's avatar
unknown committed
2974 2975 2976

#include <help_start.h>

unknown's avatar
unknown committed
2977 2978 2979 2980 2981 2982 2983 2984 2985
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();
2986
  printf("MySQL AB, by Sasha, Matt, Monty & Jani\n");
unknown's avatar
unknown committed
2987 2988 2989
  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);
2990
  my_print_help(my_long_options);
unknown's avatar
unknown committed
2991
  printf("  --no-defaults       Don't read default options from any options file.\n");
2992
  my_print_variables(my_long_options);
unknown's avatar
unknown committed
2993 2994
}

unknown's avatar
unknown committed
2995 2996
#include <help_end.h>

2997 2998 2999 3000 3001

static my_bool
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
	       char *argument)
{
3002
  switch(optid) {
3003
  case '#':
unknown's avatar
unknown committed
3004
#ifndef DBUG_OFF
3005
    DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysqltest.trace");
unknown's avatar
unknown committed
3006
#endif
3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024
    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);
3025
      DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0);
3026
      if (!(cur_file->file=
3027
            my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0))))
3028 3029
	die("Could not open %s: errno = %d", buff, errno);
      cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
3030
      cur_file->lineno= 1;
3031 3032
      break;
    }
unknown's avatar
unknown committed
3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045
  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;
    }
3046 3047 3048 3049 3050 3051
  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 */
3052
      tty_password= 0;
3053 3054 3055 3056
    }
    else
      tty_password= 1;
    break;
unknown's avatar
unknown committed
3057
#include <sslopt-case.h>
3058 3059 3060 3061 3062 3063 3064 3065 3066
  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*) "";
    }
3067 3068 3069
    if (embedded_server_arg_count == MAX_SERVER_ARGS-1 ||
        !(embedded_server_args[embedded_server_arg_count++]=
          my_strdup(argument, MYF(MY_FAE))))
3070 3071 3072 3073 3074 3075 3076 3077
    {
      die("Can't use server argument");
    }
    break;
  case 'F':
    if (read_server_arguments(argument))
      die(NullS);
    break;
3078 3079 3080 3081 3082
  case OPT_SKIP_SAFEMALLOC:
#ifdef SAFEMALLOC
    sf_malloc_quick=1;
#endif
    break;
3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093
  case 'V':
    print_version();
    exit(0);
  case '?':
    usage();
    exit(1);
  }
  return 0;
}


unknown's avatar
unknown committed
3094 3095 3096
int parse_args(int argc, char **argv)
{
  load_defaults("my",load_default_groups,&argc,&argv);
3097
  default_argv= argv;
3098

3099
  if ((handle_options(&argc, &argv, my_long_options, get_one_option)))
unknown's avatar
unknown committed
3100
    exit(1);
unknown's avatar
unknown committed
3101 3102 3103 3104 3105 3106 3107

  if (argc > 1)
  {
    usage();
    exit(1);
  }
  if (argc == 1)
3108
    db= *argv;
unknown's avatar
unknown committed
3109 3110 3111 3112 3113 3114 3115
  if (tty_password)
    pass=get_tty_password(NullS);

  return 0;
}


unknown's avatar
unknown committed
3116
/*
3117
   Write the content of str into file
unknown's avatar
unknown committed
3118

3119 3120 3121 3122 3123 3124
   SYNOPSIS
   str_to_file
   fname - name of file to truncate/create and write to
   str - content to write to file
   size - size of content witten to file
*/
unknown's avatar
unknown committed
3125

3126
static void str_to_file(const char *fname, char *str, int size)
unknown's avatar
unknown committed
3127 3128
{
  int fd;
3129 3130 3131 3132
  char buff[FN_REFLEN];
  if (!test_if_hard_path(fname))
  {
    strxmov(buff, opt_basedir, fname, NullS);
unknown's avatar
unknown committed
3133
    fname= buff;
3134 3135
  }
  fn_format(buff,fname,"","",4);
unknown's avatar
unknown committed
3136

unknown's avatar
unknown committed
3137
  if ((fd= my_open(buff, O_WRONLY | O_CREAT | O_TRUNC,
3138
		    MYF(MY_WME | MY_FFNF))) < 0)
3139
    die("Could not open %s: errno = %d", buff, errno);
3140
  if (my_write(fd, (byte*)str, size, MYF(MY_WME|MY_FNABP)))
unknown's avatar
unknown committed
3141 3142 3143 3144
    die("write failed");
  my_close(fd, MYF(0));
}

unknown's avatar
unknown committed
3145

3146
void dump_result_to_reject_file(const char *record_file, char *buf, int size)
unknown's avatar
unknown committed
3147
{
unknown's avatar
unknown committed
3148
  char reject_file[FN_REFLEN];
unknown's avatar
unknown committed
3149
  str_to_file(fn_format(reject_file, record_file,"",".reject",2), buf, size);
unknown's avatar
unknown committed
3150 3151
}

3152 3153 3154 3155 3156 3157
void dump_result_to_log_file(const char *record_file, char *buf, int size)
{
  char log_file[FN_REFLEN];
  str_to_file(fn_format(log_file, record_file,"",".log",2), buf, size);
}

3158

3159
#ifdef __WIN__
3160

3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172
DYNAMIC_ARRAY patterns;

/*
  init_win_path_patterns

  DESCRIPTION
   Setup string patterns that will be used to detect filenames that
   needs to be converted from Win to Unix format

*/

static void init_win_path_patterns()
3173
{
3174
  /* List of string patterns to match in order to find paths */
3175 3176 3177 3178
  const char* paths[] = { "$MYSQL_TEST_DIR",
                          "$MYSQL_TMP_DIR",
                          "./test/", 0 };
  int num_paths= 3;
3179 3180 3181 3182 3183 3184 3185 3186 3187
  int i;
  char* p;

  DBUG_ENTER("init_win_path_patterns");

  my_init_dynamic_array(&patterns, sizeof(const char*), 16, 16);

  /* Loop through all paths in the array */
  for (i= 0; i < num_paths; i++)
3188
  {
3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207
    VAR* v;
    if (*(paths[i]) == '$')
    {
      v= var_get(paths[i], 0, 0, 0);
      p= my_strdup(v->str_val, MYF(MY_FAE));
    }
    else
      p= my_strdup(paths[i], MYF(MY_FAE));

    if (insert_dynamic(&patterns, (gptr) &p))
        die(NullS);

    DBUG_PRINT("info", ("p: %s", p));
    while (*p)
    {
      if (*p == '/')
        *p='\\';
      p++;
    }
3208
  }
3209
  DBUG_VOID_RETURN;
3210 3211
}

3212 3213
static void free_win_path_patterns()
{
unknown's avatar
unknown committed
3214
  uint i= 0;
3215
  for (i=0 ; i < patterns.elements ; i++)
3216
  {
3217 3218
    const char** pattern= dynamic_element(&patterns, i, const char**);
    my_free((gptr) *pattern, MYF(0));
3219
  }
3220
  delete_dynamic(&patterns);
3221 3222
}

3223 3224
/*
  fix_win_paths
unknown's avatar
unknown committed
3225

3226 3227 3228 3229 3230 3231 3232 3233 3234 3235
  DESCRIPTION
   Search the string 'val' for the patterns that are known to be
   strings that contain filenames. Convert all \ to / in the
   filenames that are found.

   Ex:
   val = 'Error "c:\mysql\mysql-test\var\test\t1.frm" didn't exist'
          => $MYSQL_TEST_DIR is found by strstr
          => all \ from c:\mysql\m... until next space is converted into /
*/
3236

3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278
static void fix_win_paths(const char* val, int len)
{
  uint i;
  char *p;

  DBUG_ENTER("fix_win_paths");
  for (i= 0; i < patterns.elements; i++)
  {
    const char** pattern= dynamic_element(&patterns, i, const char**);
    DBUG_PRINT("info", ("pattern: %s", *pattern));
    /* Search for the path in string */
    while ((p= strstr(val, *pattern)))
    {
      DBUG_PRINT("info", ("Found %s in val p: %s", *pattern, p));

      while (*p && !my_isspace(charset_info, *p))
      {
        if (*p == '\\')
          *p= '/';
        p++;
      }
      DBUG_PRINT("info", ("Converted \\ to /, p: %s", p));
    }
  }
  DBUG_PRINT("exit", (" val: %s, len: %d", val, len));
  DBUG_VOID_RETURN;
}
#endif

/* Append the string to ds, with optional replace */
static void replace_dynstr_append_mem(DYNAMIC_STRING *ds,
                                      const char *val,  int len)
{
#ifdef __WIN__
  fix_win_paths(val, len);
#endif

  if (glob_replace)
    replace_strings_append(glob_replace, ds, val, len);
  else
    dynstr_append_mem(ds, val, len);
}
3279

3280 3281

/* Append zero-terminated string to ds, with optional replace */
3282 3283 3284 3285 3286
static void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val)
{
  replace_dynstr_append_mem(ds, val, strlen(val));
}

3287

3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305
/*
  Append the result for one field to the dynamic string ds
*/

static void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD* field,
                         const char* val, ulonglong len, bool is_null)
{
  if (col_idx < max_replace_column && replace_column[col_idx])
  {
    val= replace_column[col_idx];
    len= strlen(val);
  }
  else if (is_null)
  {
    val= "NULL";
    len= 4;
  }
#ifdef __WIN__
3306 3307
  else if ((field->type == MYSQL_TYPE_DOUBLE ||
            field->type == MYSQL_TYPE_FLOAT ) &&
3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338
           field->decimals >= 31)
  {
    /* Convert 1.2e+018 to 1.2e+18 and 1.2e-018 to 1.2e-18 */
    char *start= strchr(val, 'e');
    if (start && strlen(start) >= 5 &&
        (start[1] == '-' || start[1] == '+') && start[2] == '0')
    {
      start+=2; /* Now points at first '0' */
      /* Move all chars after the first '0' one step left */
      memmove(start, start + 1, strlen(start));
      len--;
    }
  }
#endif

  if (!display_result_vertically)
  {
    if (col_idx)
      dynstr_append_mem(ds, "\t", 1);
    replace_dynstr_append_mem(ds, val, (int)len);
  }
  else
  {
    dynstr_append(ds, field->name);
    dynstr_append_mem(ds, "\t", 1);
    replace_dynstr_append_mem(ds, val, (int)len);
    dynstr_append_mem(ds, "\n", 1);
  }
}


3339 3340
/*
  Append all results to the dynamic string separated with '\t'
3341
  Values may be converted with 'replace_column'
3342 3343 3344 3345 3346
*/

static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
{
  MYSQL_ROW row;
3347
  uint num_fields= mysql_num_fields(res);
3348
  MYSQL_FIELD *fields= mysql_fetch_fields(res);
3349
  ulong *lengths;
3350

3351 3352
  while ((row = mysql_fetch_row(res)))
  {
3353
    uint i;
3354 3355
    lengths = mysql_fetch_lengths(res);
    for (i = 0; i < num_fields; i++)
3356 3357
      append_field(ds, i, &fields[i],
                   (const char*)row[i], lengths[i], !row[i]);
3358 3359
    if (!display_result_vertically)
      dynstr_append_mem(ds, "\n", 1);
3360
  }
3361
  free_replace_column();
3362 3363
}

3364

3365
/*
unknown's avatar
unknown committed
3366
  Append all results from ps execution to the dynamic string separated
3367
  with '\t'. Values may be converted with 'replace_column'
3368
*/
3369

unknown's avatar
unknown committed
3370
static void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt,
3371
                               MYSQL_FIELD *fields, uint num_fields)
3372
{
3373 3374 3375
  MYSQL_BIND *bind;
  my_bool *is_null;
  ulong *length;
3376
  uint i;
3377

3378 3379 3380 3381 3382 3383 3384
  /* Allocate array with bind structs, lengths and NULL flags */
  bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND),
				MYF(MY_WME | MY_FAE | MY_ZEROFILL));
  length= (ulong*) my_malloc(num_fields * sizeof(ulong),
			     MYF(MY_WME | MY_FAE));
  is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool),
				MYF(MY_WME | MY_FAE));
3385

3386 3387
  /* Allocate data for the result of each field */
  for (i= 0; i < num_fields; i++)
unknown's avatar
unknown committed
3388
  {
3389 3390 3391 3392 3393 3394
    uint max_length= fields[i].max_length + 1;
    bind[i].buffer_type= MYSQL_TYPE_STRING;
    bind[i].buffer= (char *)my_malloc(max_length, MYF(MY_WME | MY_FAE));
    bind[i].buffer_length= max_length;
    bind[i].is_null= &is_null[i];
    bind[i].length= &length[i];
unknown's avatar
unknown committed
3395

unknown's avatar
unknown committed
3396
    DBUG_PRINT("bind", ("col[%d]: buffer_type: %d, buffer_length: %d",
3397
			i, bind[i].buffer_type, bind[i].buffer_length));
3398
  }
unknown's avatar
unknown committed
3399

3400
  if (mysql_stmt_bind_result(stmt, bind))
unknown's avatar
unknown committed
3401
    die("mysql_stmt_bind_result failed: %d: %s",
3402
	mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
3403

3404
  while (mysql_stmt_fetch(stmt) == 0)
3405
  {
3406 3407 3408
    for (i= 0; i < num_fields; i++)
      append_field(ds, i, &fields[i], (const char *) bind[i].buffer,
                   *bind[i].length, *bind[i].is_null);
3409 3410
    if (!display_result_vertically)
      dynstr_append_mem(ds, "\n", 1);
3411 3412
  }

3413 3414
  if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
    die("fetch didn't end with MYSQL_NO_DATA from statement: %d %s",
3415
	mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
3416

3417
  free_replace_column();
3418

3419
  for (i= 0; i < num_fields; i++)
3420 3421
  {
    /* Free data for output */
3422
    my_free((gptr)bind[i].buffer, MYF(MY_WME | MY_FAE));
3423 3424 3425 3426 3427
  }
  /* 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));
unknown's avatar
unknown committed
3428 3429 3430
}


3431
/*
3432
  Append metadata for fields to output
3433 3434
*/

3435
static void append_metadata(DYNAMIC_STRING *ds,
unknown's avatar
unknown committed
3436
			    MYSQL_FIELD *field,
3437
			    uint num_fields)
3438
{
3439 3440 3441 3442
  MYSQL_FIELD *field_end;
  dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t"
                "Column_alias\tType\tLength\tMax length\tIs_null\t"
                "Flags\tDecimals\tCharsetnr\n");
3443

3444 3445 3446
  for (field_end= field+num_fields ;
       field < field_end ;
       field++)
3447
  {
3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476
    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);
    longlong10_to_str((unsigned int) field->length, buff, 10);
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\t", 1);
    longlong10_to_str((unsigned 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);
3477

3478 3479 3480 3481 3482 3483 3484 3485 3486
    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);
unknown's avatar
unknown committed
3487
  }
3488 3489
}

unknown's avatar
unknown committed
3490

3491 3492 3493
/*
  Append affected row count and other info to output
*/
3494

unknown's avatar
unknown committed
3495
static void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows,
unknown's avatar
unknown committed
3496
			const char *info)
unknown's avatar
unknown committed
3497
{
3498
  char buf[40];
unknown's avatar
unknown committed
3499
  sprintf(buf,"affected rows: %llu\n", affected_rows);
3500 3501
  dynstr_append(ds, buf);
  if (info)
unknown's avatar
unknown committed
3502
  {
3503 3504 3505
    dynstr_append(ds, "info: ");
    dynstr_append(ds, info);
    dynstr_append_mem(ds, "\n", 1);
unknown's avatar
unknown committed
3506
  }
3507
}
unknown's avatar
unknown committed
3508 3509


unknown's avatar
unknown committed
3510 3511
/*
   Display the table headings with the names tab separated
3512
*/
unknown's avatar
unknown committed
3513 3514 3515

static void append_table_headings(DYNAMIC_STRING *ds,
				  MYSQL_FIELD *field,
3516 3517 3518 3519
				  uint num_fields)
{
  uint col_idx;
  for (col_idx= 0; col_idx < num_fields; col_idx++)
3520
  {
3521 3522 3523
    if (col_idx)
      dynstr_append_mem(ds, "\t", 1);
    replace_dynstr_append(ds, field[col_idx].name);
3524
  }
3525 3526 3527 3528
  dynstr_append_mem(ds, "\n", 1);
}

/*
unknown's avatar
unknown committed
3529 3530 3531 3532
  Fetch warnings from server and append to ds

  RETURN VALUE
   Number of warnings appended to ds
3533 3534
*/

unknown's avatar
unknown committed
3535
static int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql)
3536 3537
{
  uint count;
3538
  MYSQL_RES *warn_res;
3539 3540 3541
  DBUG_ENTER("append_warnings");

  if (!(count= mysql_warning_count(mysql)))
unknown's avatar
unknown committed
3542
    DBUG_RETURN(0);
3543 3544 3545 3546 3547 3548 3549

  /*
    If one day we will support execution of multi-statements
    through PS API we should not issue SHOW WARNINGS until
    we have not read all results...
  */
  DBUG_ASSERT(!mysql_more_results(mysql));
unknown's avatar
unknown committed
3550

3551 3552
  if (mysql_real_query(mysql, "SHOW WARNINGS", 13))
    die("Error running query \"SHOW WARNINGS\": %s", mysql_error(mysql));
unknown's avatar
unknown committed
3553

3554
  if (!(warn_res= mysql_store_result(mysql)))
3555 3556
    die("Warning count is %u but didn't get any warnings",
	count);
unknown's avatar
unknown committed
3557

3558 3559 3560
  append_result(ds, warn_res);
  mysql_free_result(warn_res);

3561 3562
  DBUG_PRINT("warnings", ("%s", ds->str));

unknown's avatar
unknown committed
3563
  DBUG_RETURN(count);
3564 3565 3566
}


unknown's avatar
unknown committed
3567

3568 3569
/*
  Run query using MySQL C API
unknown's avatar
unknown committed
3570

3571 3572 3573 3574
  SYNPOSIS
  run_query_normal
  mysql - mysql handle
  command - currrent command pointer
3575
  flags -flags indicating wheter to SEND and/or REAP
3576 3577 3578 3579 3580 3581 3582 3583
  query - query string to execute
  query_len - length query string to execute
  ds - output buffer wherte to store result form query

  RETURN VALUE
  error - function will not return
*/

unknown's avatar
unknown committed
3584 3585
static void run_query_normal(MYSQL *mysql, struct st_query *command,
			     int flags, char *query, int query_len,
3586
			     DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings)
3587 3588 3589 3590 3591 3592
{
  MYSQL_RES *res= 0;
  int err= 0, counter= 0;
  DBUG_ENTER("run_query_normal");
  DBUG_PRINT("enter",("flags: %d", flags));
  DBUG_PRINT("enter", ("query: '%-.60s'", query));
unknown's avatar
unknown committed
3593

3594 3595
  if (flags & QUERY_SEND)
  {
unknown's avatar
unknown committed
3596
    /*
3597
       Send the query
3598 3599 3600 3601 3602 3603 3604
     */
    if (mysql_send_query(mysql, query, query_len))
    {
      handle_error(query, command, mysql_errno(mysql), mysql_error(mysql),
		   mysql_sqlstate(mysql), ds);
      goto end;
    }
3605
  }
unknown's avatar
unknown committed
3606

3607 3608
  if (!(flags & QUERY_REAP))
    DBUG_VOID_RETURN;
unknown's avatar
unknown committed
3609

3610
  do
3611
  {
3612
    /*
unknown's avatar
unknown committed
3613
      When  on first result set, call mysql_read_query_result to retrieve
3614 3615 3616
      answer to the query sent earlier
     */
    if ((counter==0) && mysql_read_query_result(mysql))
3617
    {
3618 3619 3620 3621
      handle_error(query, command, mysql_errno(mysql), mysql_error(mysql),
		   mysql_sqlstate(mysql), ds);
      goto end;

3622
    }
3623

unknown's avatar
unknown committed
3624 3625
    /*
       Store the result. If res is NULL, use mysql_field_count to
3626 3627 3628
       determine if that was expected
     */
    if (!(res= mysql_store_result(mysql)) && mysql_field_count(mysql))
unknown's avatar
unknown committed
3629
    {
3630 3631
      handle_error(query, command, mysql_errno(mysql), mysql_error(mysql),
		   mysql_sqlstate(mysql), ds);
3632 3633
      goto end;
    }
3634

3635
    if (!disable_result_log)
3636
    {
unknown's avatar
unknown committed
3637
      ulonglong affected_rows;    /* Ok to be undef if 'disable_info' is set */
unknown's avatar
unknown committed
3638
      LINT_INIT(affected_rows);
unknown's avatar
unknown committed
3639

3640
      if (res)
3641
      {
3642
	MYSQL_FIELD *fields= mysql_fetch_fields(res);
3643
	uint num_fields= mysql_num_fields(res);
3644

3645
	if (display_metadata)
3646
          append_metadata(ds, fields, num_fields);
3647

3648
	if (!display_result_vertically)
3649 3650
	  append_table_headings(ds, fields, num_fields);

3651
	append_result(ds, res);
3652
      }
unknown's avatar
unknown committed
3653

unknown's avatar
unknown committed
3654
      /*
3655
        Need to call mysql_affected_rows() before the "new"
unknown's avatar
unknown committed
3656 3657 3658
        query to find the warnings
      */
      if (!disable_info)
unknown's avatar
unknown committed
3659
        affected_rows= mysql_affected_rows(mysql);
unknown's avatar
unknown committed
3660

3661 3662 3663 3664 3665
      /*
        Add all warnings to the result. We can't do this if we are in
        the middle of processing results from multi-statement, because
        this will break protocol.
      */
3666
      if (!disable_warnings && !mysql_more_results(mysql))
3667
      {
3668
	if (append_warnings(ds_warnings, mysql) || ds_warnings->length)
3669 3670
	{
	  dynstr_append_mem(ds, "Warnings:\n", 10);
3671
	  dynstr_append_mem(ds, ds_warnings->str, ds_warnings->length);
3672
	}
3673
      }
3674

unknown's avatar
unknown committed
3675
      if (!disable_info)
3676
	append_info(ds, affected_rows, mysql_info(mysql));
3677
    }
3678

3679 3680 3681 3682
    if (res)
      mysql_free_result(res);
    counter++;
  } while (!(err= mysql_next_result(mysql)));
3683 3684
  if (err > 0)
  {
3685 3686 3687
    /* We got an error from mysql_next_result, maybe expected */
    handle_error(query, command, mysql_errno(mysql), mysql_error(mysql),
		 mysql_sqlstate(mysql), ds);
3688 3689
    goto end;
  }
3690
  DBUG_ASSERT(err == -1); /* Successful and there are no more results */
3691

3692
  /* If we come here the query is both executed and read successfully */
3693
  handle_no_error(command);
3694

3695
end:
3696
  free_replace();
3697 3698 3699 3700 3701 3702

  /*
    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.
  */
3703
  var_set_errno(mysql_errno(mysql));
3704
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
3705 3706 3707
}


3708
/*
3709
  Handle errors which occurred during execution
3710 3711

  SYNOPSIS
3712
    handle_error()
3713 3714
      query - query string
      q     - query context
3715 3716 3717
      err_errno - error number
      err_error - error message
      err_sqlstate - sql state
3718 3719 3720 3721 3722 3723 3724
      ds    - dynamic string which is used for output buffer

  NOTE
    If there is an unexpected error this function will abort mysqltest
    immediately.

  RETURN VALUE
3725
    error - function will not return
3726 3727
*/

3728 3729 3730
static void handle_error(const char *query, struct st_query *q,
			 unsigned int err_errno, const char *err_error,
			 const char *err_sqlstate, DYNAMIC_STRING *ds)
3731 3732
{
  uint i;
unknown's avatar
unknown committed
3733

3734
  DBUG_ENTER("handle_error");
3735 3736

  if (q->require_file)
unknown's avatar
unknown committed
3737 3738 3739 3740 3741 3742 3743 3744 3745
  {
    /*
      The query after a "--require" failed. This is fine as long the server
      returned a valid reponse. Don't allow 2013 or 2006 to trigger an
      abort_not_supported_test
     */
    if (err_errno == CR_SERVER_LOST ||
        err_errno == CR_SERVER_GONE_ERROR)
      die("require query '%s' failed: %d: %s", query, err_errno, err_error);
3746
    abort_not_supported_test();
unknown's avatar
unknown committed
3747
  }
unknown's avatar
unknown committed
3748

3749
  if (q->abort_on_error)
3750
    die("query '%s' failed: %d: %s", query, err_errno, err_error);
3751 3752

  for (i= 0 ; (uint) i < q->expected_errors ; i++)
3753
  {
3754 3755 3756 3757
    if (((q->expected_errno[i].type == ERR_ERRNO) &&
         (q->expected_errno[i].code.errnum == err_errno)) ||
        ((q->expected_errno[i].type == ERR_SQLSTATE) &&
         (strcmp(q->expected_errno[i].code.sqlstate, err_sqlstate) == 0)))
3758
    {
3759
      if (!disable_result_log)
3760
      {
3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774
        if (q->expected_errors == 1)
        {
          /* Only log error if there is one possible error */
          dynstr_append_mem(ds, "ERROR ", 6);
          replace_dynstr_append(ds, err_sqlstate);
          dynstr_append_mem(ds, ": ", 2);
          replace_dynstr_append(ds, err_error);
          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");
3775
      }
3776
      /* OK */
3777
      DBUG_VOID_RETURN;
3778
    }
3779
  }
3780

3781
  DBUG_PRINT("info",("i: %d  expected_errors: %d", i, q->expected_errors));
3782

3783 3784 3785 3786 3787 3788 3789 3790
  if (!disable_result_log)
  {
    dynstr_append_mem(ds, "ERROR ",6);
    replace_dynstr_append(ds, err_sqlstate);
    dynstr_append_mem(ds, ": ", 2);
    replace_dynstr_append(ds, err_error);
    dynstr_append_mem(ds, "\n", 1);
  }
3791

3792 3793 3794
  if (i)
  {
    if (q->expected_errno[0].type == ERR_ERRNO)
3795 3796
      die("query '%s' failed with wrong errno %d: '%s', instead of %d...",
          q->query, err_errno, err_error, q->expected_errno[0].code.errnum);
3797
    else
3798 3799 3800
      die("query '%s' failed with wrong sqlstate %s: '%s', instead of %s...",
          q->query, err_sqlstate, err_error,
	  q->expected_errno[0].code.sqlstate);
3801
  }
3802

3803
  DBUG_VOID_RETURN;
3804 3805 3806 3807
}


/*
3808
  Handle absence of errors after execution
3809 3810

  SYNOPSIS
3811
    handle_no_error()
3812 3813 3814
      q - context of query

  RETURN VALUE
3815
    error - function will not return
3816 3817
*/

3818
static void handle_no_error(struct st_query *q)
3819
{
3820
  DBUG_ENTER("handle_no_error");
3821 3822 3823 3824 3825

  if (q->expected_errno[0].type == ERR_ERRNO &&
      q->expected_errno[0].code.errnum != 0)
  {
    /* Error code we wanted was != 0, i.e. not an expected success */
3826 3827
    die("query '%s' succeeded - should have failed with errno %d...",
        q->query, q->expected_errno[0].code.errnum);
3828 3829 3830 3831 3832
  }
  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 */
3833 3834
    die("query '%s' succeeded - should have failed with sqlstate %s...",
        q->query, q->expected_errno[0].code.sqlstate);
3835 3836
  }

3837
  DBUG_VOID_RETURN;
3838 3839
}

3840 3841

/*
3842
  Run query using prepared statement C API
unknown's avatar
unknown committed
3843

3844 3845 3846 3847 3848 3849
  SYNPOSIS
  run_query_stmt
  mysql - mysql handle
  command - currrent command pointer
  query - query string to execute
  query_len - length query string to execute
unknown's avatar
unknown committed
3850
  ds - output buffer where to store result form query
3851

3852 3853
  RETURN VALUE
  error - function will not return
3854 3855
*/

unknown's avatar
unknown committed
3856
static void run_query_stmt(MYSQL *mysql, struct st_query *command,
3857 3858
			   char *query, int query_len, DYNAMIC_STRING *ds,
			   DYNAMIC_STRING *ds_warnings)
3859 3860 3861
{
  MYSQL_RES *res= NULL;     /* Note that here 'res' is meta data result set */
  MYSQL_STMT *stmt;
3862
  DYNAMIC_STRING ds_prepare_warnings;
unknown's avatar
unknown committed
3863
  DYNAMIC_STRING ds_execute_warnings;
3864
  DBUG_ENTER("run_query_stmt");
3865
  DBUG_PRINT("query", ("'%-.60s'", query));
3866 3867

  /*
unknown's avatar
unknown committed
3868
    Init a new stmt if it's not already one created for this connection
3869
  */
3870
  if(!(stmt= cur_con->stmt))
3871
  {
3872 3873 3874
    if (!(stmt= mysql_stmt_init(mysql)))
      die("unable to init stmt structure");
    cur_con->stmt= stmt;
3875 3876
  }

unknown's avatar
unknown committed
3877 3878
  /* Init dynamic strings for warnings */
  if (!disable_warnings)
3879
  {
unknown's avatar
unknown committed
3880 3881
    init_dynamic_string(&ds_prepare_warnings, NULL, 0, 256);
    init_dynamic_string(&ds_execute_warnings, NULL, 0, 256);
3882 3883 3884
  }

  /*
3885
    Prepare the query
3886
  */
3887
  if (mysql_stmt_prepare(stmt, query, query_len))
3888
  {
3889
    handle_error(query, command,  mysql_stmt_errno(stmt),
3890
                 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
3891
    goto end;
3892 3893 3894
  }

  /*
unknown's avatar
unknown committed
3895 3896
    Get the warnings from mysql_stmt_prepare and keep them in a
    separate string
3897
  */
3898 3899
  if (!disable_warnings)
    append_warnings(&ds_prepare_warnings, mysql);
3900 3901

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

3906 3907
  if (cursor_protocol_enabled)
  {
3908 3909 3910
    /*
      Use cursor when retrieving result
    */
unknown's avatar
unknown committed
3911
    ulong type= CURSOR_TYPE_READ_ONLY;
3912
    if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type))
unknown's avatar
unknown committed
3913
      die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s",
3914
          mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
3915
  }
3916

3917 3918
  /*
    Execute the query
3919
  */
unknown's avatar
unknown committed
3920
  if (mysql_stmt_execute(stmt))
3921
  {
3922
    handle_error(query, command, mysql_stmt_errno(stmt),
3923
                 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
3924
    goto end;
3925 3926
  }

3927 3928 3929 3930 3931 3932 3933
  /*
    When running in cursor_protocol get the warnings from execute here
    and keep them in a separate string for later.
  */
  if (cursor_protocol_enabled && !disable_warnings)
    append_warnings(&ds_execute_warnings, mysql);

3934 3935 3936 3937 3938 3939 3940
  /*
    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;
3941
    if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one))
unknown's avatar
unknown committed
3942
      die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s",
3943
          mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
3944 3945 3946 3947 3948 3949
  }

  /*
    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!
  */
3950
  if (mysql_stmt_store_result(stmt))
3951
  {
3952
    handle_error(query, command, mysql_stmt_errno(stmt),
3953
                 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
3954
    goto end;
3955
  }
3956

unknown's avatar
unknown committed
3957
  /* If we got here the statement was both executed and read successfully */
3958 3959
  handle_no_error(command);
  if (!disable_result_log)
3960
  {
3961 3962 3963 3964 3965 3966
    /*
      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.
    */
3967
    if ((res= mysql_stmt_result_metadata(stmt)) != NULL)
unknown's avatar
unknown committed
3968
    {
3969 3970 3971
      /* Take the column count from meta info */
      MYSQL_FIELD *fields= mysql_fetch_fields(res);
      uint num_fields= mysql_num_fields(res);
3972

3973
      if (display_metadata)
3974
        append_metadata(ds, fields, num_fields);
3975

3976
      if (!display_result_vertically)
3977
        append_table_headings(ds, fields, num_fields);
3978

unknown's avatar
unknown committed
3979
      append_stmt_result(ds, stmt, fields, num_fields);
3980

3981
      mysql_free_result(res);     /* Free normal result set with meta data */
3982

unknown's avatar
unknown committed
3983 3984
      /* Clear prepare warnings */
      dynstr_set(&ds_prepare_warnings, NULL);
unknown's avatar
unknown committed
3985 3986
    }
    else
3987
    {
3988 3989 3990
      /*
	This is a query without resultset
      */
3991 3992
    }

3993
    if (!disable_warnings)
3994
    {
unknown's avatar
unknown committed
3995
      /* Get the warnings from execute */
3996

unknown's avatar
unknown committed
3997 3998
      /* Append warnings to ds - if there are any */
      if (append_warnings(&ds_execute_warnings, mysql) ||
3999 4000 4001
          ds_execute_warnings.length ||
          ds_prepare_warnings.length ||
          ds_warnings->length)
4002
      {
4003
        dynstr_append_mem(ds, "Warnings:\n", 10);
4004 4005 4006
	if (ds_warnings->length)
	  dynstr_append_mem(ds, ds_warnings->str,
			    ds_warnings->length);
unknown's avatar
unknown committed
4007 4008 4009 4010 4011 4012
	if (ds_prepare_warnings.length)
	  dynstr_append_mem(ds, ds_prepare_warnings.str,
			    ds_prepare_warnings.length);
	if (ds_execute_warnings.length)
	  dynstr_append_mem(ds, ds_execute_warnings.str,
			    ds_execute_warnings.length);
4013 4014 4015 4016
      }
    }

    if (!disable_info)
unknown's avatar
unknown committed
4017
      append_info(ds, mysql_affected_rows(mysql), mysql_info(mysql));
4018 4019 4020 4021 4022

  }

end:
  free_replace();
unknown's avatar
unknown committed
4023

4024
  if (!disable_warnings)
unknown's avatar
unknown committed
4025
  {
4026
    dynstr_free(&ds_prepare_warnings);
unknown's avatar
unknown committed
4027 4028
    dynstr_free(&ds_execute_warnings);
  }
4029 4030

  /*
4031 4032 4033
    We save the return code (mysql_stmt_errno(stmt)) 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.
4034
  */
4035
  var_set_errno(mysql_stmt_errno(stmt));
unknown's avatar
unknown committed
4036
#ifndef BUG15518_FIXED
4037
  mysql_stmt_close(stmt);
unknown's avatar
unknown committed
4038 4039
  cur_con->stmt= NULL;
#endif
4040
  DBUG_VOID_RETURN;
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

/*
  Create a util connection if one does not already exists
  and use that to run the query
  This is done to avoid implict commit when creating/dropping objects such
  as view, sp etc.
*/

static int util_query(MYSQL* org_mysql, const char* query){

  MYSQL* mysql;
  DBUG_ENTER("util_query");

  if(!(mysql= cur_con->util_mysql))
  {
    DBUG_PRINT("info", ("Creating util_mysql"));
    if (!(mysql= mysql_init(mysql)))
      die("Failed in mysql_init()");

    if (safe_connect(mysql, org_mysql->host, org_mysql->user,
		     org_mysql->passwd, org_mysql->db, org_mysql->port,
		     org_mysql->unix_socket))
      die("Could not open util connection: %d %s",
	  mysql_errno(mysql), mysql_error(mysql));

    cur_con->util_mysql= mysql;
  }

  return mysql_query(mysql, query);
}



4077 4078
/*
  Run query
4079

4080 4081 4082
  flags control the phased/stages of query execution to be performed
  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
4083

4084 4085 4086 4087
  SYNPOSIS
  run_query
  mysql - mysql handle
  command - currrent command pointer
unknown's avatar
unknown committed
4088

4089
*/
4090

4091
static void run_query(MYSQL *mysql, struct st_query *command, int flags)
4092
{
4093
  DYNAMIC_STRING *ds;
4094
  DYNAMIC_STRING ds_result;
4095
  DYNAMIC_STRING ds_warnings;
4096 4097 4098
  DYNAMIC_STRING eval_query;
  char *query;
  int query_len;
4099 4100
  my_bool view_created= 0, sp_created= 0;
  my_bool complete_query= ((flags & QUERY_SEND) && (flags & QUERY_REAP));
4101

4102 4103
  init_dynamic_string(&ds_warnings, NULL, 0, 256);

4104 4105 4106 4107
  /*
    Evaluate query if this is an eval command
   */
  if (command->type == Q_EVAL)
4108
  {
4109
    init_dynamic_string(&eval_query, "", 16384, 65536);
4110
    do_eval(&eval_query, command->query, FALSE);
4111 4112
    query = eval_query.str;
    query_len = eval_query.length;
4113 4114 4115
  }
  else
  {
4116 4117
    query = command->query;
    query_len = strlen(query);
4118 4119
  }

4120
  /*
unknown's avatar
unknown committed
4121
    When command->record_file is set the output of _this_ query
4122 4123 4124 4125 4126
    should be compared with an already existing file
    Create a temporary dynamic string to contain the output from
    this query.
   */
  if (command->record_file[0])
4127
  {
4128 4129
    init_dynamic_string(&ds_result, "", 16384, 65536);
    ds= &ds_result;
4130 4131 4132 4133
  }
  else
    ds= &ds_res;

unknown's avatar
unknown committed
4134 4135
  /*
     Log the query into the output buffer
4136
  */
4137
  if (!disable_query_log && (flags & QUERY_SEND))
4138
  {
4139
    replace_dynstr_append_mem(ds, query, query_len);
4140 4141 4142 4143
    dynstr_append_mem(ds, delimiter, delimiter_length);
    dynstr_append_mem(ds, "\n", 1);
  }

4144 4145 4146
  if (view_protocol_enabled &&
      complete_query &&
      match_re(&view_re, query))
4147
  {
4148
    /*
4149
       Create the query as a view.
unknown's avatar
unknown committed
4150
       Use replace since view can exist from a failed mysqltest run
4151
    */
unknown's avatar
unknown committed
4152 4153 4154
    DYNAMIC_STRING query_str;
    init_dynamic_string(&query_str,
			"CREATE OR REPLACE VIEW mysqltest_tmp_v AS ",
4155 4156
			query_len+64, 256);
    dynstr_append_mem(&query_str, query, query_len);
4157
    if (util_query(mysql, query_str.str))
4158 4159 4160 4161 4162
    {
      /*
	Failed to create the view, this is not fatal
	just run the query the normal way
       */
unknown's avatar
unknown committed
4163
      DBUG_PRINT("view_create_error",
4164 4165
		 ("Failed to create view '%s': %d: %s", query_str.str,
		  mysql_errno(mysql), mysql_error(mysql)));
4166

unknown's avatar
unknown committed
4167 4168 4169
      /* Log error to create view */
      verbose_msg("Failed to create view '%s' %d: %s", query_str.str,
		  mysql_errno(mysql), mysql_error(mysql));
4170 4171 4172 4173 4174 4175 4176
    }
    else
    {
      /*
	Yes, it was possible to create this query as a view
       */
      view_created= 1;
unknown's avatar
unknown committed
4177
      query= (char*)"SELECT * FROM mysqltest_tmp_v";
4178
      query_len = strlen(query);
4179

4180 4181 4182 4183
      /*
	 Collect warnings from create of the view that should otherwise
         have been produced when the SELECT was executed
      */
4184
      append_warnings(&ds_warnings, cur_con->util_mysql);
4185 4186
    }

4187
    dynstr_free(&query_str);
4188 4189 4190

  }

4191 4192 4193
  if (sp_protocol_enabled &&
      complete_query &&
      match_re(&sp_re, query))
4194
  {
4195
    /*
4196
      Create the query as a stored procedure
unknown's avatar
unknown committed
4197
      Drop first since sp can exist from a failed mysqltest run
4198
    */
4199
    DYNAMIC_STRING query_str;
unknown's avatar
unknown committed
4200
    init_dynamic_string(&query_str,
4201
			"DROP PROCEDURE IF EXISTS mysqltest_tmp_sp;",
4202
			query_len+64, 256);
4203
    util_query(mysql, query_str.str);
4204 4205
    dynstr_set(&query_str, "CREATE PROCEDURE mysqltest_tmp_sp()\n");
    dynstr_append_mem(&query_str, query, query_len);
4206
    if (util_query(mysql, query_str.str))
4207
    {
4208
      /*
unknown's avatar
unknown committed
4209
	Failed to create the stored procedure for this query,
4210 4211 4212 4213 4214
	this is not fatal just run the query the normal way
      */
      DBUG_PRINT("sp_create_error",
		 ("Failed to create sp '%s': %d: %s", query_str.str,
		  mysql_errno(mysql), mysql_error(mysql)));
unknown's avatar
unknown committed
4215 4216 4217 4218

      /* Log error to create sp */
      verbose_msg("Failed to create sp '%s' %d: %s", query_str.str,
		  mysql_errno(mysql), mysql_error(mysql));
4219

4220
    }
4221
    else
4222
    {
4223
      sp_created= 1;
unknown's avatar
unknown committed
4224 4225

      query= (char*)"CALL mysqltest_tmp_sp()";
4226
      query_len = strlen(query);
4227
    }
4228
    dynstr_free(&query_str);
4229 4230 4231
  }

  /*
4232
    Find out how to run this query
4233

unknown's avatar
unknown committed
4234
    Always run with normal C API if it's not a complete
4235
    SEND + REAP
4236

4237
    If it is a '?' in the query it may be a SQL level prepared
4238
    statement already and we can't do it twice
4239
  */
4240
  if (ps_protocol_enabled &&
4241 4242
      complete_query &&
      match_re(&ps_re, query))
4243
    run_query_stmt(mysql, command, query, query_len, ds, &ds_warnings);
4244
  else
4245 4246
    run_query_normal(mysql, command, flags, query, query_len,
		     ds, &ds_warnings);
unknown's avatar
unknown committed
4247

4248
  if (sp_created)
4249
  {
4250
    if (util_query(mysql, "DROP PROCEDURE mysqltest_tmp_sp "))
4251
      die("Failed to drop sp: %d: %s", mysql_errno(mysql), mysql_error(mysql));
4252 4253
  }

4254
  if (view_created)
4255
  {
4256
    if (util_query(mysql, "DROP VIEW mysqltest_tmp_v "))
unknown's avatar
unknown committed
4257
      die("Failed to drop view: %d: %s",
4258
	  mysql_errno(mysql), mysql_error(mysql));
4259 4260
  }

unknown's avatar
unknown committed
4261
  if (command->record_file[0])
4262 4263
  {

unknown's avatar
unknown committed
4264 4265
    /* A result file was specified for _this_ query  */
    if (record)
4266
    {
unknown's avatar
unknown committed
4267 4268 4269 4270 4271
      /*
	 Recording in progress
         Dump the output from _this_ query to the specified record_file
      */
      str_to_file(command->record_file, ds->str, ds->length);
4272

unknown's avatar
unknown committed
4273
    } else {
4274

unknown's avatar
unknown committed
4275 4276 4277 4278 4279
      /*
	The output from _this_ query should be checked against an already
	existing file which has been specified using --require or --result
      */
      check_result(ds, command->record_file, command->require_file);
4280 4281 4282
    }
  }

4283
  dynstr_free(&ds_warnings);
4284 4285
  if (ds == &ds_result)
    dynstr_free(&ds_result);
4286
  if (command->type == Q_EVAL)
4287 4288 4289 4290 4291
    dynstr_free(&eval_query);
}


/****************************************************************************\
4292
 *  Functions to detect different SQL statements
4293 4294
\****************************************************************************/

4295
static char *re_eprint(int err)
4296
{
4297
  static char epbuf[100];
unknown's avatar
unknown committed
4298
  size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL,
4299 4300 4301
			  epbuf, sizeof(epbuf));
  assert(len <= sizeof(epbuf));
  return(epbuf);
4302 4303
}

4304
static void init_re_comp(my_regex_t *re, const char* str)
4305
{
4306 4307 4308
  int err= my_regcomp(re, str, (REG_EXTENDED | REG_ICASE | REG_NOSUB),
                      &my_charset_latin1);
  if (err)
4309
  {
4310 4311
    char erbuf[100];
    int len= my_regerror(err, re, erbuf, sizeof(erbuf));
unknown's avatar
unknown committed
4312
    die("error %s, %d/%d `%s'\n",
4313
	re_eprint(err), len, (int)sizeof(erbuf), erbuf);
4314 4315 4316
  }
}

4317
static void init_re(void)
4318
{
unknown's avatar
unknown committed
4319 4320
  /*
     Filter for queries that can be run using the
4321 4322
     MySQL Prepared Statements C API
  */
4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336
  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:]])";

unknown's avatar
unknown committed
4337 4338
  /*
     Filter for queries that can be run using the
4339 4340 4341 4342
     Stored procedures
  */
  const char *sp_re_str =ps_re_str;

unknown's avatar
unknown committed
4343
  /*
4344 4345 4346 4347 4348 4349 4350 4351 4352
     Filter for queries that can be run as views
  */
  const char *view_re_str =
    "^("
    "[[:space:]]*SELECT[[:space:]])";

  init_re_comp(&ps_re, ps_re_str);
  init_re_comp(&sp_re, sp_re_str);
  init_re_comp(&view_re, view_re_str);
4353 4354 4355
}


4356
static int match_re(my_regex_t *re, char *str)
4357
{
4358
  int err= my_regexec(re, str, (size_t)0, NULL, 0);
4359 4360 4361 4362 4363

  if (err == 0)
    return 1;
  else if (err == REG_NOMATCH)
    return 0;
4364

4365 4366
  {
    char erbuf[100];
4367 4368 4369
    int len= my_regerror(err, re, erbuf, sizeof(erbuf));
    die("error %s, %d/%d `%s'\n",
	re_eprint(err), len, (int)sizeof(erbuf), erbuf);
4370
  }
4371
  return 0;
4372 4373
}

4374
static void free_re(void)
4375
{
unknown's avatar
unknown committed
4376
  my_regfree(&ps_re);
4377 4378
  my_regfree(&sp_re);
  my_regfree(&view_re);
unknown's avatar
unknown committed
4379
  my_regex_end();
4380 4381 4382 4383
}

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

4384
void get_query_type(struct st_query* q)
4385
{
4386 4387
  char save;
  uint type;
unknown's avatar
unknown committed
4388 4389
  DBUG_ENTER("get_query_type");

4390
  if (!parsing_disabled && *q->query == '}')
4391 4392
  {
    q->type = Q_END_BLOCK;
unknown's avatar
unknown committed
4393
    DBUG_VOID_RETURN;
4394 4395
  }
  if (q->type != Q_COMMENT_WITH_COMMAND)
4396
    q->type= parsing_disabled ? Q_COMMENT : Q_QUERY;
4397

4398 4399
  save=q->query[q->first_word_len];
  q->query[q->first_word_len]=0;
4400
  type=find_type(q->query, &command_typelib, 1+2);
4401
  q->query[q->first_word_len]=save;
4402
  if (type > 0)
4403
  {
unknown's avatar
unknown committed
4404
    q->type=(enum enum_commands) type;		/* Found command */
4405 4406
    /*
      If queries are disabled, only recognize
unknown's avatar
unknown committed
4407
      --enable_parsing and --disable_parsing
4408 4409 4410 4411 4412
    */
    if (parsing_disabled && q->type != Q_ENABLE_PARSING &&
        q->type != Q_DISABLE_PARSING)
      q->type= Q_COMMENT;
  }
4413
  else if (q->type == Q_COMMENT_WITH_COMMAND &&
unknown's avatar
unknown committed
4414
	   q->first_word_len &&
4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429
           q->query[q->first_word_len-1] == ';')
  {
    /*
       Detect comment with command using extra delimiter
       Ex --disable_query_log;
                             ^ Extra delimiter causing the command
                               to be skipped
    */
    save= q->query[q->first_word_len-1];
    q->query[q->first_word_len-1]= 0;
    type= find_type(q->query, &command_typelib, 1+2);
    q->query[q->first_word_len-1]= save;
    if (type > 0)
      die("Extra delimiter \";\" found");
  }
unknown's avatar
unknown committed
4430
  DBUG_VOID_RETURN;
4431
}
unknown's avatar
unknown committed
4432

unknown's avatar
unknown committed
4433

unknown's avatar
unknown committed
4434
static byte *get_var_key(const byte* var, uint* len,
unknown's avatar
unknown committed
4435
			 my_bool __attribute__((unused)) t)
unknown's avatar
unknown committed
4436 4437 4438 4439 4440 4441 4442
{
  register char* key;
  key = ((VAR*)var)->name;
  *len = ((VAR*)var)->name_len;
  return (byte*)key;
}

unknown's avatar
unknown committed
4443
static VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
unknown's avatar
unknown committed
4444 4445 4446
		     int val_len)
{
  int val_alloc_len;
unknown's avatar
unknown committed
4447
  VAR *tmp_var;
4448
  if (!name_len && name)
unknown's avatar
unknown committed
4449
    name_len = strlen(name);
4450
  if (!val_len && val)
unknown's avatar
unknown committed
4451 4452
    val_len = strlen(val) ;
  val_alloc_len = val_len + 16; /* room to grow */
4453
  if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var)
unknown's avatar
unknown committed
4454
						 + name_len+1, MYF(MY_WME))))
unknown's avatar
unknown committed
4455
    die("Out of memory");
unknown's avatar
unknown committed
4456

4457
  tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0;
4458
  tmp_var->alloced = (v == 0);
4459

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

4463 4464
  if (name)
    strmake(tmp_var->name, name, name_len);
4465
  if (val)
4466
    strmake(tmp_var->str_val, val, val_len);
unknown's avatar
unknown committed
4467 4468 4469
  tmp_var->name_len = name_len;
  tmp_var->str_val_len = val_len;
  tmp_var->alloced_len = val_alloc_len;
4470
  tmp_var->int_val = (val) ? atoi(val) : 0;
unknown's avatar
unknown committed
4471 4472 4473 4474
  tmp_var->int_dirty = 0;
  return tmp_var;
}

unknown's avatar
unknown committed
4475
static void var_free(void *v)
unknown's avatar
unknown committed
4476
{
unknown's avatar
unknown committed
4477
  my_free(((VAR*) v)->str_val, MYF(MY_WME));
unknown's avatar
unknown committed
4478 4479
  if (((VAR*)v)->alloced)
   my_free((char*) v, MYF(MY_WME));
unknown's avatar
unknown committed
4480 4481 4482
}


4483
static VAR* var_from_env(const char *name, const char *def_val)
unknown's avatar
unknown committed
4484
{
unknown's avatar
unknown committed
4485 4486
  const char *tmp;
  VAR *v;
4487
  if (!(tmp = getenv(name)))
unknown's avatar
unknown committed
4488
    tmp = def_val;
unknown's avatar
unknown committed
4489

4490
  v = var_init(0, name, strlen(name), tmp, strlen(tmp));
unknown's avatar
SCRUM  
unknown committed
4491
  my_hash_insert(&var_hash, (byte*)v);
4492
  return v;
4493
}
unknown's avatar
unknown committed
4494

4495

unknown's avatar
unknown committed
4496
static void init_var_hash(MYSQL *mysql)
unknown's avatar
unknown committed
4497
{
unknown's avatar
unknown committed
4498
  VAR *v;
4499
  DBUG_ENTER("init_var_hash");
unknown's avatar
unknown committed
4500
  if (hash_init(&var_hash, charset_info,
unknown's avatar
unknown committed
4501
                1024, 0, 0, get_var_key, var_free, MYF(0)))
unknown's avatar
unknown committed
4502
    die("Variable hash initialization failed");
4503 4504
  my_hash_insert(&var_hash, (byte*) var_init(0,"BIG_TEST", 0,
                                             (opt_big_test) ? "1" : "0", 0));
unknown's avatar
unknown committed
4505
  v= var_init(0,"MAX_TABLES", 0, (sizeof(ulong) == 4) ? "31" : "62",0);
unknown's avatar
SCRUM  
unknown committed
4506
  my_hash_insert(&var_hash, (byte*) v);
unknown's avatar
unknown committed
4507
  v= var_init(0,"SERVER_VERSION", 0, mysql_get_server_info(mysql), 0);
4508
  my_hash_insert(&var_hash, (byte*) v);  v= var_init(0,"DB", 2, db, 0);
unknown's avatar
unknown committed
4509
  my_hash_insert(&var_hash, (byte*) v);
4510
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
4511
}
4512

4513 4514
static void mark_progress(int line)
{
unknown's avatar
unknown committed
4515
#ifdef NOT_YET
4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541
  static FILE* fp = NULL;
  static double first;

  struct timeval tv;
  double now;

  if (!fp)
  {

    fp = fopen("/tmp/mysqltest_progress.log", "wt");

    if (!fp)
    {
      abort();
    }

    gettimeofday(&tv, NULL);
    first = tv.tv_sec * 1e6 + tv.tv_usec;
  }

  gettimeofday(&tv, NULL);
  now = tv.tv_sec * 1e6 + tv.tv_usec;

  fprintf(fp, "%d %f\n", parser.current_line, (now - first) / 1e6);
#endif
}
4542

unknown's avatar
unknown committed
4543
int main(int argc, char **argv)
unknown's avatar
unknown committed
4544
{
unknown's avatar
unknown committed
4545
  struct st_query *q;
4546 4547
  my_bool require_file=0, q_send_flag=0, abort_flag= 0,
          query_executed= 0;
4548
  char save_file[FN_REFLEN];
4549
  MY_STAT res_info;
4550 4551
  MY_INIT(argv[0]);

unknown's avatar
unknown committed
4552 4553 4554
  /* Use all time until exit if no explicit 'start_timer' */
  timer_start= timer_now();

4555
  save_file[0]=0;
4556
  TMPDIR[0]=0;
4557 4558

  /* Init cons */
unknown's avatar
unknown committed
4559 4560 4561 4562
  memset(cons, 0, sizeof(cons));
  cons_end = cons + MAX_CONS;
  next_con = cons + 1;
  cur_con = cons;
unknown's avatar
unknown committed
4563

4564
  /* Init file stack */
unknown's avatar
unknown committed
4565
  memset(file_stack, 0, sizeof(file_stack));
4566 4567
  file_stack_end= file_stack + MAX_INCLUDE_DEPTH - 1;
  cur_file= file_stack;
4568

4569
  /* Init block stack */
4570
  memset(block_stack, 0, sizeof(block_stack));
4571
  block_stack_end= block_stack + BLOCK_STACK_DEPTH - 1;
unknown's avatar
unknown committed
4572 4573 4574 4575
  cur_block= block_stack;
  cur_block->ok= TRUE; /* Outer block should always be executed */
  cur_block->cmd= cmd_none;

4576 4577 4578 4579 4580
  my_init_dynamic_array(&q_lines, sizeof(struct st_query*), INIT_Q_LINES,
		     INIT_Q_LINES);

  memset(&master_pos, 0, sizeof(master_pos));

4581
  init_dynamic_string(&ds_res, "", 0, 65536);
unknown's avatar
unknown committed
4582
  parse_args(argc, argv);
4583 4584

  DBUG_PRINT("info",("result_file: '%s'", result_file ? result_file : ""));
4585 4586 4587
  if (mysql_server_init(embedded_server_arg_count,
			embedded_server_args,
			(char**) embedded_server_groups))
unknown's avatar
unknown committed
4588
    die("Can't initialize MySQL server");
4589
  if (cur_file == file_stack && cur_file->file == 0)
4590
  {
4591 4592
    cur_file->file= stdin;
    cur_file->file_name= my_strdup("<stdin>", MYF(MY_WME));
4593
    cur_file->lineno= 1;
4594
  }
4595 4596 4597
#ifndef EMBEDDED_LIBRARY
  if (manager_host)
    init_manager();
4598
#endif
4599 4600 4601 4602 4603 4604 4605
  init_re();
  ps_protocol_enabled= ps_protocol;
  sp_protocol_enabled= sp_protocol;
  view_protocol_enabled= view_protocol;
  cursor_protocol_enabled= cursor_protocol;
  /* Cursor protcol implies ps protocol */
  if (cursor_protocol_enabled)
4606
    ps_protocol_enabled= 1;
4607

4608
  if (!( mysql_init(&cur_con->mysql)))
unknown's avatar
unknown committed
4609
    die("Failed in mysql_init()");
unknown's avatar
unknown committed
4610 4611
  if (opt_compress)
    mysql_options(&cur_con->mysql,MYSQL_OPT_COMPRESS,NullS);
4612
  mysql_options(&cur_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
unknown's avatar
unknown committed
4613
  mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name);
unknown's avatar
unknown committed
4614

unknown's avatar
unknown committed
4615
#ifdef HAVE_OPENSSL
4616
  opt_ssl_verify_server_cert= TRUE; /* Always on in mysqltest */
unknown's avatar
unknown committed
4617
  if (opt_use_ssl)
4618
  {
unknown's avatar
unknown committed
4619 4620
    mysql_ssl_set(&cur_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
		  opt_ssl_capath, opt_ssl_cipher);
4621 4622 4623
    mysql_options(&cur_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
                  &opt_ssl_verify_server_cert);
  }
unknown's avatar
unknown committed
4624
#endif
4625

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

unknown's avatar
unknown committed
4629
  if (safe_connect(&cur_con->mysql, host, user, pass, db, port, unix_sock))
4630 4631
    die("Could not open connection '%s': %d %s", cur_con->name,
	mysql_errno(&cur_con->mysql), mysql_error(&cur_con->mysql));
unknown's avatar
unknown committed
4632

unknown's avatar
unknown committed
4633 4634
  init_var_hash(&cur_con->mysql);

4635
#ifdef __WIN__
4636
  init_tmp_sh_file();
4637 4638 4639
  init_win_path_patterns();
#endif

4640 4641 4642 4643 4644
  /*
    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
  */
4645 4646
  var_set_errno(-1);

unknown's avatar
unknown committed
4647
  while (!abort_flag && !read_query(&q))
4648 4649 4650 4651
  {
    int current_line_inc = 1, processed = 0;
    if (q->type == Q_UNKNOWN || q->type == Q_COMMENT_WITH_COMMAND)
      get_query_type(q);
unknown's avatar
unknown committed
4652
    if (cur_block->ok)
unknown's avatar
unknown committed
4653
    {
4654
      q->last_argument= q->first_argument;
4655 4656
      processed = 1;
      switch (q->type) {
4657
      case Q_CONNECT:
4658
        do_connect(q);
4659
        break;
4660
      case Q_CONNECTION: select_connection(q); break;
unknown's avatar
unknown committed
4661
      case Q_DISCONNECT:
4662
      case Q_DIRTY_CLOSE:
unknown's avatar
unknown committed
4663
	close_connection(q); break;
4664
      case Q_RPL_PROBE: do_rpl_probe(q); break;
4665
      case Q_ENABLE_RPL_PARSE:	 do_enable_rpl_parse(q); break;
unknown's avatar
unknown committed
4666
      case Q_DISABLE_RPL_PARSE:  do_disable_rpl_parse(q); break;
4667
      case Q_ENABLE_QUERY_LOG:   disable_query_log=0; break;
unknown's avatar
unknown committed
4668
      case Q_DISABLE_QUERY_LOG:  disable_query_log=1; break;
4669 4670
      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
4671 4672
      case Q_ENABLE_RESULT_LOG:  disable_result_log=0; break;
      case Q_DISABLE_RESULT_LOG: disable_result_log=1; break;
4673 4674
      case Q_ENABLE_WARNINGS:    disable_warnings=0; break;
      case Q_DISABLE_WARNINGS:   disable_warnings=1; break;
4675 4676
      case Q_ENABLE_PS_WARNINGS:    disable_ps_warnings=0; break;
      case Q_DISABLE_PS_WARNINGS:   disable_ps_warnings=1; break;
4677 4678
      case Q_ENABLE_INFO:        disable_info=0; break;
      case Q_DISABLE_INFO:       disable_info=1; break;
4679
      case Q_ENABLE_METADATA:    display_metadata=1; break;
4680
      case Q_DISABLE_METADATA:   display_metadata=0; break;
4681
      case Q_SOURCE: do_source(q); break;
4682 4683
      case Q_SLEEP: do_sleep(q, 0); break;
      case Q_REAL_SLEEP: do_sleep(q, 1); break;
4684
      case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(q); break;
4685
      case Q_REQUIRE_MANAGER: do_require_manager(q); break;
4686
#ifndef EMBEDDED_LIBRARY
4687 4688
      case Q_SERVER_START: do_server_start(q); break;
      case Q_SERVER_STOP: do_server_stop(q); break;
4689
#endif
unknown's avatar
unknown committed
4690 4691
      case Q_INC: do_modify_var(q, DO_INC); break;
      case Q_DEC: do_modify_var(q, DO_DEC); break;
4692
      case Q_ECHO: do_echo(q); query_executed= 1; break;
4693
      case Q_SYSTEM: do_system(q); break;
4694 4695 4696
      case Q_DELIMITER:
	strmake(delimiter, q->first_argument, sizeof(delimiter) - 1);
	delimiter_length= strlen(delimiter);
4697
        q->last_argument= q->first_argument+delimiter_length;
4698
	break;
4699 4700 4701 4702 4703 4704
      case Q_DISPLAY_VERTICAL_RESULTS:
        display_result_vertically= TRUE;
        break;
      case Q_DISPLAY_HORIZONTAL_RESULTS:
	display_result_vertically= FALSE;
        break;
4705
      case Q_LET: do_let(q); break;
4706
      case Q_EVAL_RESULT:
4707
        eval_result = 1; break;
4708
      case Q_EVAL:
4709
	if (q->query == q->query_buf)
unknown's avatar
unknown committed
4710
        {
unknown's avatar
unknown committed
4711
	  q->query= q->first_argument;
unknown's avatar
unknown committed
4712 4713
          q->first_word_len= 0;
        }
4714
	/* fall through */
4715
      case Q_QUERY_VERTICAL:
4716
      case Q_QUERY_HORIZONTAL:
4717 4718
      {
	my_bool old_display_result_vertically= display_result_vertically;
unknown's avatar
unknown committed
4719
	/* fix up query pointer if this is first iteration for this line */
4720 4721
	if (q->query == q->query_buf)
	  q->query += q->first_word_len + 1;
4722
	display_result_vertically= (q->type==Q_QUERY_VERTICAL);
4723 4724 4725 4726 4727 4728
	if (save_file[0])
	{
	  strmov(q->record_file,save_file);
	  q->require_file=require_file;
	  save_file[0]=0;
	}
4729
	run_query(&cur_con->mysql, q, QUERY_REAP|QUERY_SEND);
4730
	display_result_vertically= old_display_result_vertically;
4731
        q->last_argument= q->end;
4732
        query_executed= 1;
4733 4734
	break;
      }
4735
      case Q_QUERY:
4736
      case Q_REAP:
4737
      {
4738 4739 4740 4741 4742
	/*
	  We read the result always regardless of the mode for both full
	  query and read-result only (reap)
	*/
	int flags = QUERY_REAP;
4743
	if (q->type != Q_REAP) /* for a full query, enable the send stage */
unknown's avatar
unknown committed
4744
	  flags |= QUERY_SEND;
unknown's avatar
unknown committed
4745 4746 4747 4748 4749
	if (q_send_flag)
	{
	  flags= QUERY_SEND;
	  q_send_flag=0;
	}
4750
	if (save_file[0])
4751
	{
4752 4753 4754
	  strmov(q->record_file,save_file);
	  q->require_file=require_file;
	  save_file[0]=0;
4755
	}
4756
	run_query(&cur_con->mysql, q, flags);
4757
	query_executed= 1;
4758
        q->last_argument= q->end;
unknown's avatar
unknown committed
4759
	break;
4760
      }
unknown's avatar
unknown committed
4761
      case Q_SEND:
unknown's avatar
unknown committed
4762 4763
	if (!q->query[q->first_word_len])
	{
unknown's avatar
unknown committed
4764
	  /* This happens when we use 'send' on its own line */
unknown's avatar
unknown committed
4765 4766 4767
	  q_send_flag=1;
	  break;
	}
unknown's avatar
unknown committed
4768
	/* fix up query pointer if this is first iteration for this line */
unknown's avatar
unknown committed
4769
	if (q->query == q->query_buf)
4770
	  q->query += q->first_word_len;
4771
	/*
unknown's avatar
unknown committed
4772
	  run_query() can execute a query partially, depending on the flags.
4773 4774 4775
	  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.
4776
	 */
4777
	run_query(&cur_con->mysql, q, QUERY_SEND);
4778
	query_executed= 1;
4779
        q->last_argument= q->end;
unknown's avatar
unknown committed
4780
	break;
4781 4782 4783 4784
      case Q_RESULT:
	get_file_name(save_file,q);
	require_file=0;
	break;
unknown's avatar
unknown committed
4785
      case Q_ERROR:
4786
        global_expected_errors=get_errcodes(global_expected_errno,q);
unknown's avatar
unknown committed
4787
	break;
4788 4789 4790 4791
      case Q_REQUIRE:
	get_file_name(save_file,q);
	require_file=1;
	break;
unknown's avatar
unknown committed
4792 4793 4794
      case Q_REPLACE:
	get_replace(q);
	break;
4795 4796 4797
      case Q_REPLACE_COLUMN:
	get_replace_column(q);
	break;
4798 4799
      case Q_SAVE_MASTER_POS: do_save_master_pos(); break;
      case Q_SYNC_WITH_MASTER: do_sync_with_master(q); break;
4800 4801 4802 4803
      case Q_SYNC_SLAVE_WITH_MASTER:
      {
	do_save_master_pos();
	if (*q->first_argument)
4804
	  select_connection(q);
4805
	else
4806 4807
	  select_connection_name("slave");
	do_sync_with_master2(0);
4808 4809
	break;
      }
4810
      case Q_COMMENT:				/* Ignore row */
4811
      case Q_COMMENT_WITH_COMMAND:
4812
        q->last_argument= q->end;
4813
	break;
4814 4815 4816
      case Q_PING:
	(void) mysql_ping(&cur_con->mysql);
	break;
4817
      case Q_EXEC:
unknown's avatar
unknown committed
4818
	do_exec(q);
4819
	query_executed= 1;
4820
	break;
unknown's avatar
unknown committed
4821 4822 4823 4824 4825 4826 4827 4828 4829
      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;
4830
      case Q_CHARACTER_SET:
unknown's avatar
unknown committed
4831 4832
	set_charset(q);
	break;
4833 4834 4835 4836 4837 4838
      case Q_DISABLE_PS_PROTOCOL:
        ps_protocol_enabled= 0;
        break;
      case Q_ENABLE_PS_PROTOCOL:
        ps_protocol_enabled= ps_protocol;
        break;
4839
      case Q_DISABLE_RECONNECT:
4840 4841 4842
      {
        my_bool reconnect= 0;
        mysql_options(&cur_con->mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
4843
        break;
4844
      }
4845
      case Q_ENABLE_RECONNECT:
4846 4847 4848
      {
        my_bool reconnect= 1;
        mysql_options(&cur_con->mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
4849
        break;
4850
      }
4851 4852 4853 4854 4855
      case Q_DISABLE_PARSING:
        parsing_disabled++;
        break;
      case Q_ENABLE_PARSING:
        /*
unknown's avatar
unknown committed
4856
          Ensure we don't get parsing_disabled < 0 as this would accidentally
4857 4858 4859 4860 4861
          disable code we don't want to have disabled
        */
        if (parsing_disabled > 0)
          parsing_disabled--;
        break;
4862

unknown's avatar
unknown committed
4863 4864 4865
      case Q_EXIT:
        abort_flag= 1;
        break;
4866 4867 4868 4869

      default:
        processed= 0;
        break;
4870 4871
      }
    }
4872

4873 4874
    if (!processed)
    {
4875
      current_line_inc= 0;
unknown's avatar
unknown committed
4876
      switch (q->type) {
unknown's avatar
unknown committed
4877 4878
      case Q_WHILE: do_block(cmd_while, q); break;
      case Q_IF: do_block(cmd_if, q); break;
4879 4880 4881
      case Q_END_BLOCK: do_done(q); break;
      default: current_line_inc = 1; break;
      }
unknown's avatar
unknown committed
4882
    }
4883 4884
    else
      check_eol_junk(q->last_argument);
unknown's avatar
unknown committed
4885

4886 4887 4888 4889 4890 4891 4892 4893 4894
    if (q->type != Q_ERROR)
    {
      /*
        As soon as any non "error" command has been executed,
        the array with expected errors should be cleared
      */
      global_expected_errors= 0;
      bzero((gptr) global_expected_errno, sizeof(global_expected_errno));
    }
unknown's avatar
unknown committed
4895

4896
    parser.current_line += current_line_inc;
4897
    mark_progress(parser.current_line);
4898 4899
  }

4900 4901
  start_lineno= 0;

4902
  /*
unknown's avatar
unknown committed
4903 4904 4905
    The whole test has been executed _sucessfully_.
    Time to compare result or save it to record file.
    The entire output from test is now kept in ds_res.
unknown's avatar
unknown committed
4906
  */
4907
  if (ds_res.length)
4908
  {
4909 4910
    if (result_file)
    {
4911 4912 4913 4914 4915
      if (record)
      {
	/* Dump the output from test to result file */
	str_to_file(result_file, ds_res.str, ds_res.length);
      }
4916
      else
4917
      {
unknown's avatar
unknown committed
4918 4919 4920 4921
	/* Check that the output from test is equal to result file
	   - detect missing result file
	   - detect zero size result file
	 */
4922 4923
	check_result(&ds_res, result_file, 0);
      }
4924
    }
4925
    else
4926
    {
unknown's avatar
unknown committed
4927
      /* No result_file specified to compare with, print to stdout */
4928 4929
      printf("%s", ds_res.str);
    }
4930
  }
unknown's avatar
unknown committed
4931
  else
unknown's avatar
unknown committed
4932
  {
unknown's avatar
unknown committed
4933
    die("The test didn't produce any output");
unknown's avatar
unknown committed
4934
  }
4935

unknown's avatar
unknown committed
4936
  if (!query_executed && result_file && my_stat(result_file, &res_info, 0))
unknown's avatar
unknown committed
4937
  {
unknown's avatar
unknown committed
4938 4939 4940
    /*
      my_stat() successful on result file. Check if we have not run a
      single query, but we do have a result file that contains data.
4941 4942
      Note that we don't care, if my_stat() fails. For example, for a
      non-existing or non-readable file, we assume it's fine to have
unknown's avatar
unknown committed
4943 4944 4945
      no query output from the test file, e.g. regarded as no error.
    */
    die("No queries executed but result file found!");
unknown's avatar
unknown committed
4946
  }
4947

unknown's avatar
unknown committed
4948

4949
  dynstr_free(&ds_res);
4950

unknown's avatar
unknown committed
4951 4952
  if (!got_end_timer)
    timer_output();				/* No end_timer cmd, end it */
4953
  free_used_memory();
4954
  my_end(MY_CHECK_ERROR);
4955 4956 4957 4958 4959 4960

  /* Yes, if we got this far the test has suceeded! Sakila smiles */
  if (!silent)
    printf("ok\n");
  exit(0);
  return 0;				/* Keep compiler happy */
unknown's avatar
unknown committed
4961
}
unknown's avatar
unknown committed
4962

4963

4964 4965 4966 4967 4968 4969
/*
  Read arguments for embedded server and put them into
  embedded_server_args_count and embedded_server_args[]
*/


unknown's avatar
unknown committed
4970
static int read_server_arguments(const char *name)
4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986
{
  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
4987
  if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008
    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
5009

unknown's avatar
unknown committed
5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036
/****************************************************************************\
 *
 *  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)
  {
5037
    char buf[32], *end;
unknown's avatar
unknown committed
5038
    ulonglong timer= timer_now() - timer_start;
5039 5040
    end= longlong2str(timer, buf, 10);
    str_to_file(timer_file,buf, (int) (end-buf));
unknown's avatar
unknown committed
5041 5042 5043 5044 5045 5046 5047 5048
  }
}

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

unknown's avatar
unknown committed
5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061
/****************************************************************************
* 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 {
5062
  bool	 found;
unknown's avatar
unknown committed
5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167
  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 {
5168 5169
  uint	*bits;				/* Pointer to used sets */
  short next[LAST_CHAR_CODE];		/* Pointer to next sets */
unknown's avatar
unknown committed
5170 5171
  uint	found_len;			/* Best match to date */
  int	found_offset;
5172 5173
  uint	table_offset;
  uint	size_of_bits;			/* For convinience */
unknown's avatar
unknown committed
5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201
} 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);
5202 5203
static void internal_set_bit(REP_SET *set, uint bit);
static void internal_clear_bit(REP_SET *set, uint bit);
unknown's avatar
unknown committed
5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279
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] == '^')
    {
5280
      internal_set_bit(start_states,states+1);
unknown's avatar
unknown committed
5281 5282 5283 5284 5285 5286 5287 5288
      if (!from[i][2])
      {
	start_states->table_offset=i;
	start_states->found_offset=1;
      }
    }
    else if (from[i][0] == '\\' && from[i][1] == '$')
    {
5289 5290
      internal_set_bit(start_states,states);
      internal_set_bit(word_states,states);
unknown's avatar
unknown committed
5291 5292 5293 5294 5295 5296 5297 5298
      if (!from[i][2] && start_states->table_offset == (uint) ~0)
      {
	start_states->table_offset=i;
	start_states->found_offset=0;
      }
    }
    else
    {
5299
      internal_set_bit(word_states,states);
unknown's avatar
unknown committed
5300
      if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2]))
5301
	internal_set_bit(start_states,states+1);
unknown's avatar
unknown committed
5302
      else
5303
	internal_set_bit(start_states,states);
unknown's avatar
unknown committed
5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408
    }
    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)
5409
	      internal_set_bit(new_set,i+1);		/* To next set */
unknown's avatar
unknown committed
5410
	    else
5411
	      internal_set_bit(new_set,i);
unknown's avatar
unknown committed
5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427
	  }
	}
	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)))
5428
	      internal_clear_bit(new_set,i);
unknown's avatar
unknown committed
5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576
	    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;
}

5577
static void internal_set_bit(REP_SET *set, uint bit)
unknown's avatar
unknown committed
5578 5579 5580 5581 5582
{
  set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
  return;
}

5583
static void internal_clear_bit(REP_SET *set, uint bit)
unknown's avatar
unknown committed
5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634
{
  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
unknown's avatar
unknown committed
5635
	   free given set, else put in given set in sets and return its
unknown's avatar
unknown committed
5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653
	   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.
unknown's avatar
unknown committed
5654
	   Pos returned is -offset-2 in found_set_structure because it is
unknown's avatar
unknown committed
5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701
	   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;
}


5702 5703 5704
/* Replace strings while appending to ds */
void replace_strings_append(REPLACE *rep, DYNAMIC_STRING* ds,
                            const char *str, int len)
unknown's avatar
unknown committed
5705 5706 5707
{
  reg1 REPLACE *rep_pos;
  reg2 REPLACE_STRING *rep_str;
5708 5709
  const char *start, *from;
  DBUG_ENTER("replace_strings_append");
unknown's avatar
unknown committed
5710

5711
  start= from= str;
unknown's avatar
unknown committed
5712
  rep_pos=rep+1;
5713
  for (;;)
unknown's avatar
unknown committed
5714
  {
5715 5716
    /* Loop through states */
    DBUG_PRINT("info", ("Looping through states"));
unknown's avatar
unknown committed
5717
    while (!rep_pos->found)
5718 5719 5720
      rep_pos= rep_pos->next[(uchar) *from++];

    /* Does this state contain a string to be replaced */
unknown's avatar
unknown committed
5721 5722
    if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string)
    {
5723 5724 5725 5726
      /* No match found */
      dynstr_append_mem(ds, start, from - start - 1);
      DBUG_PRINT("exit", ("Found no more string to replace, appended: %s", start));
      DBUG_VOID_RETURN;
unknown's avatar
unknown committed
5727
    }
5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740

    /* Found a string that needs to be replaced */
    DBUG_PRINT("info", ("found: %d, to_offset: %d, from_offset: %d, string: %s",
                        rep_str->found, rep_str->to_offset,
                        rep_str->from_offset, rep_str->replace_string));

    /* Append part of original string before replace string */
    dynstr_append_mem(ds, start, (from - rep_str->to_offset) - start);

    /* Append replace string */
    dynstr_append_mem(ds, rep_str->replace_string,
                      strlen(rep_str->replace_string));

unknown's avatar
unknown committed
5741
    if (!*(from-=rep_str->from_offset) && rep_pos->found != 2)
5742 5743 5744 5745 5746 5747 5748
    {
      /* End of from string */
      DBUG_PRINT("exit", ("Found end of from string"));
      DBUG_VOID_RETURN;
    }
    DBUG_ASSERT(from <= str+len);
    start= from;
unknown's avatar
unknown committed
5749 5750 5751 5752 5753
    rep_pos=rep;
  }
}


5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787
/****************************************************************************
 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)
5788
    die("Missing argument in %s", q->query);
5789 5790 5791 5792 5793 5794 5795 5796 5797 5798

  /* 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)
5799
      die("Wrong column number to replace_column in '%s'", q->query);
5800
    if (!*from)
5801
      die("Wrong number of arguments to replace_column in '%s'", q->query);
5802 5803 5804 5805 5806 5807
    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));
5808
  q->last_argument= q->end;
5809
}
unknown's avatar
unknown committed
5810 5811


5812 5813