mysqltest.c 138 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.5"
46

unknown's avatar
unknown committed
47
#include <my_global.h>
unknown's avatar
unknown committed
48
#include <mysql_embed.h>
49 50 51 52
#include <my_sys.h>
#include <m_string.h>
#include <mysql.h>
#include <mysql_version.h>
unknown's avatar
unknown committed
53
#include <mysqld_error.h>
54 55
#include <m_ctype.h>
#include <my_dir.h>
56
#include <errmsg.h>                       /* Error codes */
unknown's avatar
unknown committed
57
#include <hash.h>
58
#include <my_getopt.h>
unknown's avatar
unknown committed
59 60
#include <stdarg.h>
#include <sys/stat.h>
unknown's avatar
unknown committed
61
#include <violite.h>
62
#include <regex.h>                        /* Our own version of lib */
unknown's avatar
unknown committed
63
#ifdef HAVE_SYS_WAIT_H
64
#include <sys/wait.h>
unknown's avatar
unknown committed
65 66 67 68
#endif
#ifndef WEXITSTATUS
# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
#endif
69 70
/* MAX_QUERY is 256K -- there is a test in sp-big that is >128K */
#define MAX_QUERY     (256*1024)
unknown's avatar
unknown committed
71
#define MAX_VAR_NAME	256
72
#define MAX_COLUMNS	256
73
#define PAD_SIZE	128
74
#define MAX_CONS	128
unknown's avatar
unknown committed
75
#define MAX_INCLUDE_DEPTH 16
unknown's avatar
unknown committed
76
#define LAZY_GUESS_BUF_SIZE 8192
77 78
#define INIT_Q_LINES	  1024
#define MIN_VAR_ALLOC	  32
79
#define BLOCK_STACK_DEPTH  32
80
#define MAX_EXPECTED_ERRORS 10
unknown's avatar
unknown committed
81 82
#define QUERY_SEND  1
#define QUERY_REAP  2
83 84 85
#ifndef MYSQL_MANAGER_PORT
#define MYSQL_MANAGER_PORT 23546
#endif
unknown's avatar
unknown committed
86
#define MAX_SERVER_ARGS 64
87

unknown's avatar
unknown committed
88 89 90 91 92 93 94
/*
  Sometimes in a test the client starts before
  the server - to solve the problem, we try again
  after some sleep if connection fails the first
  time
*/
#define CON_RETRY_SLEEP 2
95
#define MAX_CON_TRIES	5
unknown's avatar
unknown committed
96

97
#define SLAVE_POLL_INTERVAL 300000 /* 0.3 of a sec */
98 99
#define DEFAULT_DELIMITER ";"
#define MAX_DELIMITER 16
100

101 102 103
#define RESULT_OK 0
#define RESULT_CONTENT_MISMATCH 1
#define RESULT_LENGTH_MISMATCH 2
104

105
enum {OPT_MANAGER_USER=256,OPT_MANAGER_HOST,OPT_MANAGER_PASSWD,
unknown's avatar
unknown committed
106 107
      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,
108
      OPT_SSL_CIPHER,OPT_PS_PROTOCOL};
unknown's avatar
unknown committed
109

110 111
/* ************************************************************************ */
/*
112 113 114 115 116
  The list of error codes to --error are stored in an internal array of
  structs. This struct can hold numeric SQL error codes or SQLSTATE codes
  as strings. The element next to the last active element in the list is
  set to type ERR_EMPTY. When an SQL statement return an error we use
  this list to check if this  is an expected error.
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
*/
 
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;

136 137 138 139 140 141 142 143 144 145 146
typedef struct
{
  const char *name;
  long        code;
} st_error;

static st_error global_error[] = {
#include <mysqld_ername.h>
  { 0, 0 }
};

147 148 149 150
static match_err global_expected_errno[MAX_EXPECTED_ERRORS];
static uint global_expected_errors;

/* ************************************************************************ */
unknown's avatar
unknown committed
151

152
static int record = 0, opt_sleep=0;
unknown's avatar
unknown committed
153
static char *db = 0, *pass=0;
154
const char *user = 0, *host = 0, *unix_sock = 0, *opt_basedir="./";
155
static int port = 0;
156 157
static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0;
static my_bool tty_password= 0, ps_protocol= 0, ps_protocol_enabled= 0;
158
static int parsing_disabled= 0;
159
static uint start_lineno, *lineno;
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 174 175 176 177 178 179
typedef struct
{
  FILE* file;
  const char *file_name;
} test_file;

static test_file file_stack[MAX_INCLUDE_DEPTH];
static test_file* cur_file;
static test_file* file_stack_end;

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

unknown's avatar
unknown committed
185 186 187 188 189 190 191 192 193 194
/* 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;
195

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

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

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

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

211 212 213 214 215 216
static regex_t ps_re; /* Holds precompiled re for valid PS statements */
static void ps_init_re(void);
static int ps_match_re(char *);
static char *ps_eprint(int);
static void ps_free_reg(void);

217 218 219 220 221 222 223
static const char *embedded_server_groups[] = {
  "server",
  "embedded",
  "mysqltest_SERVER",
  NullS
};

224 225
DYNAMIC_ARRAY q_lines;

unknown's avatar
unknown committed
226 227
#include "sslopt-vars.h"

228
typedef struct
unknown's avatar
unknown committed
229 230 231 232 233
{
  char file[FN_REFLEN];
  ulong pos;
} MASTER_POS ;

unknown's avatar
unknown committed
234 235 236 237 238 239
struct connection
{
  MYSQL mysql;
  char *name;
};

unknown's avatar
unknown committed
240 241 242 243
typedef struct
{
  int read_lines,current_line;
} PARSER;
244

245 246
MYSQL_RES *last_result=0;

247
PARSER parser;
unknown's avatar
unknown committed
248
MASTER_POS master_pos;
unknown's avatar
unknown committed
249 250
/* if set, all results are concated and compared against this file */
const char *result_file = 0;
251

252
typedef struct
253
{
unknown's avatar
unknown committed
254
  char *name;
unknown's avatar
unknown committed
255
  int name_len;
unknown's avatar
unknown committed
256
  char *str_val;
257 258 259 260
  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
261
  int alloced;
262 263
} VAR;

unknown's avatar
unknown committed
264
#if defined(__NETWARE__) || defined(__WIN__)
unknown's avatar
unknown committed
265 266 267 268 269 270
/*
  Netware doesn't proved environment variable substitution that is done
  by the shell in unix environments. We do this in the following function:
*/

static char *subst_env_var(const char *cmd);
271
static FILE *my_popen(const char *cmd, const char *mode);
272
#undef popen
unknown's avatar
unknown committed
273 274 275
#define popen(A,B) my_popen((A),(B))
#endif /* __NETWARE__ */

276 277
VAR var_reg[10];
/*Perl/shell-like variable registers */
unknown's avatar
unknown committed
278
HASH var_hash;
279
my_bool disable_query_log=0, disable_result_log=0, disable_warnings=0;
280
my_bool disable_ps_warnings= 0;
281
my_bool disable_info= 1;			/* By default off */
282
my_bool abort_on_error= 1;
283

unknown's avatar
unknown committed
284 285 286
struct connection cons[MAX_CONS];
struct connection* cur_con, *next_con, *cons_end;

unknown's avatar
unknown committed
287 288 289
  /* Add new commands before Q_UNKNOWN !*/

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

325 326
Q_UNKNOWN,			       /* Unknown command.   */
Q_COMMENT,			       /* Comments, ignored. */
327
Q_COMMENT_WITH_COMMAND
unknown's avatar
unknown committed
328 329
};

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

342 343 344 345 346
const char *command_names[]=
{
  "connection",
  "query",
  "connect",
unknown's avatar
unknown committed
347 348
  /* the difference between sleep and real_sleep is that sleep will use
     the delay from command line (--sleep) if there is one.
349
     real_sleep always uses delay from mysqltest's command line argument.
unknown's avatar
unknown committed
350 351 352 353
     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
   */
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
  "sleep",
  "real_sleep",
  "inc",
  "dec",
  "source",
  "disconnect",
  "let",
  "echo",
  "while",
  "end",
  "system",
  "result",
  "require",
  "save_master_pos",
  "sync_with_master",
369
  "sync_slave_with_master",
370 371 372 373 374
  "error",
  "send",
  "reap",
  "dirty_close",
  "replace_result",
375
  "replace_column",
376 377 378 379 380 381 382 383 384 385 386 387 388 389
  "ping",
  "eval",
  "rpl_probe",
  "enable_rpl_parse",
  "disable_rpl_parse",
  "eval_result",
  "enable_query_log",
  "disable_query_log",
  "enable_result_log",
  "disable_result_log",
  "server_start",
  "server_stop",
  "require_manager",
  "wait_for_slave_to_stop",
390 391
  "enable_warnings",
  "disable_warnings",
392 393
  "enable_ps_warnings",
  "disable_ps_warnings",
394
  "enable_info",
395
  "disable_info",
396 397
  "enable_metadata",
  "disable_metadata",
398
  "exec",
399
  "delimiter",
400 401
  "disable_abort_on_error",
  "enable_abort_on_error",
402
  "vertical_results",
403
  "horizontal_results",
404
  "query_vertical",
405
  "query_horizontal",
unknown's avatar
unknown committed
406 407
  "start_timer",
  "end_timer",
unknown's avatar
unknown committed
408
  "character_set",
409 410
  "disable_ps_protocol",
  "enable_ps_protocol",
unknown's avatar
unknown committed
411
  "exit",
412 413
  "disable_reconnect",
  "enable_reconnect",
unknown's avatar
unknown committed
414
  "if",
415 416
  "disable_parsing",
  "enable_parsing",
unknown's avatar
unknown committed
417
  0
418 419 420
};

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

423
DYNAMIC_STRING ds_res;
unknown's avatar
unknown committed
424
static void die(const char *fmt, ...);
unknown's avatar
unknown committed
425
static void init_var_hash();
426
static VAR* var_from_env(const char *, const char *);
427
static byte* get_var_key(const byte* rec, uint* len, my_bool t);
unknown's avatar
unknown committed
428
static VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
unknown's avatar
unknown committed
429 430 431
		     int val_len);

static void var_free(void* v);
432

unknown's avatar
unknown committed
433 434
int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname);
void reject_dump(const char *record_file, char *buf, int size);
unknown's avatar
unknown committed
435

436
int close_connection(struct st_query*);
unknown's avatar
unknown committed
437
static void set_charset(struct st_query*);
438 439
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
440 441
int eval_expr(VAR* v, const char *p, const char** p_end);
static int read_server_arguments(const char *name);
442

443
/* Definitions for replace result */
unknown's avatar
unknown committed
444 445 446 447 448

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. */
449
  uint	array_allocs,max_count,length,max_length;
unknown's avatar
unknown committed
450 451 452 453 454 455
} POINTER_ARRAY;

struct st_replace;
struct st_replace *init_replace(my_string *from, my_string *to, uint count,
				my_string word_end_chars);
uint replace_strings(struct st_replace *rep, my_string *start,
unknown's avatar
unknown committed
456
		     uint *max_length, const char *from);
457
void free_replace();
unknown's avatar
unknown committed
458 459 460 461
static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name);
void free_pointer_array(POINTER_ARRAY *pa);
static int initialize_replace_buffer(void);
static void free_replace_buffer(void);
462
static void do_eval(DYNAMIC_STRING *query_eval, const char *query);
unknown's avatar
unknown committed
463
void str_to_file(const char *fname, char *str, int size);
464
int do_server_op(struct st_query *q,const char *op);
unknown's avatar
unknown committed
465 466 467 468

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

471 472 473 474 475 476 477
/* 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
478
/* Disable functions that only exist in MySQL 4.0 */
unknown's avatar
SCRUM  
unknown committed
479
#if MYSQL_VERSION_ID < 40000
unknown's avatar
unknown committed
480 481 482
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; }
483
my_bool mysql_rpl_probe(MYSQL *mysql __attribute__((unused))) { return 1; }
unknown's avatar
unknown committed
484
#endif
485 486
static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
				      int len);
487
static void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val);
488 489 490 491
static int 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 int handle_no_error(struct st_query *q);
492

493
static void do_eval(DYNAMIC_STRING* query_eval, const char *query)
494
{
495
  const char *p;
496 497 498
  register char c;
  register int escaped = 0;
  VAR* v;
unknown's avatar
unknown committed
499
  DBUG_ENTER("do_eval");
unknown's avatar
unknown committed
500

501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
  for (p= query; (c = *p); ++p)
  {
    switch(c) {
    case '$':
      if (escaped)
      {
	escaped = 0;
	dynstr_append_mem(query_eval, p, 1);
      }
      else
      {
	if (!(v = var_get(p, &p, 0, 0)))
	  die("Bad variable in eval");
	dynstr_append_mem(query_eval, v->str_val, v->str_val_len);
      }
      break;
    case '\\':
      if (escaped)
      {
	escaped = 0;
	dynstr_append_mem(query_eval, p, 1);
      }
      else
	escaped = 1;
      break;
    default:
      dynstr_append_mem(query_eval, p, 1);
      break;
529
    }
530
  }
unknown's avatar
unknown committed
531
  DBUG_VOID_RETURN;
532
}
unknown's avatar
unknown committed
533

534

535 536
static void close_cons()
{
537
  DBUG_ENTER("close_cons");
538 539
  if (last_result)
    mysql_free_result(last_result);
540 541 542 543 544 545 546 547
  for (--next_con; next_con >= cons; --next_con)
  {
    mysql_close(&next_con->mysql);
    my_free(next_con->name, MYF(MY_ALLOW_ZERO_PTR));
  }
  DBUG_VOID_RETURN;
}

548

549 550
static void close_files()
{
unknown's avatar
unknown committed
551
  DBUG_ENTER("close_files");
552
  for (; cur_file != (file_stack-1) ; cur_file--)
553
  {
554 555 556 557 558
    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
559 560
  }
  DBUG_VOID_RETURN;
561 562
}

563

564 565 566 567
static void free_used_memory()
{
  uint i;
  DBUG_ENTER("free_used_memory");
568
#ifndef EMBEDDED_LIBRARY
569 570
  if (manager)
    mysql_manager_close(manager);
571
#endif
572 573
  close_cons();
  close_files();
unknown's avatar
unknown committed
574
  hash_free(&var_hash);
unknown's avatar
unknown committed
575

576 577 578
  for (i=0 ; i < q_lines.elements ; i++)
  {
    struct st_query **q= dynamic_element(&q_lines, i, struct st_query**);
579
    my_free((gptr) (*q)->query_buf,MYF(MY_ALLOW_ZERO_PTR));
580 581
    my_free((gptr) (*q),MYF(0));
  }
582
  for (i=0; i < 10; i++)
unknown's avatar
unknown committed
583 584 585 586
  {
    if (var_reg[i].alloced_len)
      my_free(var_reg[i].str_val, MYF(MY_WME));
  }
587 588
  while (embedded_server_arg_count > 1)
    my_free(embedded_server_args[--embedded_server_arg_count],MYF(0));
589 590
  delete_dynamic(&q_lines);
  dynstr_free(&ds_res);
unknown's avatar
unknown committed
591
  free_replace();
592
  free_replace_column();
593 594
  my_free(pass,MYF(MY_ALLOW_ZERO_PTR));
  free_defaults(default_argv);
unknown's avatar
unknown committed
595
  mysql_server_end();
596 597
  if (ps_protocol)
    ps_free_reg();
598
  DBUG_VOID_RETURN;
599 600
}

601
static void die(const char *fmt, ...)
602 603
{
  va_list args;
604
  DBUG_ENTER("die");
605
  va_start(args, fmt);
606 607
  if (fmt)
  {
608 609
    fprintf(stderr, "mysqltest: ");
    if (cur_file && cur_file != file_stack)
610
      fprintf(stderr, "In included file \"%s\": ",
611
              cur_file->file_name);
612
    fprintf(stderr, "At line %u: ", start_lineno);
613 614
    vfprintf(stderr, fmt, args);
    fprintf(stderr, "\n");
unknown's avatar
unknown committed
615
    fflush(stderr);
616
  }
617
  va_end(args);
618
  free_used_memory();
619
  my_end(MY_CHECK_ERROR);
620 621 622
  exit(1);
}

623 624
/* Note that we will get some memory leaks when calling this! */

625 626
static void abort_not_supported_test()
{
627
  DBUG_ENTER("abort_not_supported_test");
628 629 630
  fprintf(stderr, "This test is not supported by this installation\n");
  if (!silent)
    printf("skipped\n");
631
  free_used_memory();
632
  my_end(MY_CHECK_ERROR);
unknown's avatar
unknown committed
633
  exit(62);
634 635
}

636
static void verbose_msg(const char *fmt, ...)
637 638
{
  va_list args;
639 640 641
  DBUG_ENTER("verbose_msg");
  if (!verbose)
    DBUG_VOID_RETURN;
642 643 644

  va_start(args, fmt);

645
  fprintf(stderr, "mysqltest: At line %u: ", start_lineno);
646 647 648
  vfprintf(stderr, fmt, args);
  fprintf(stderr, "\n");
  va_end(args);
649
  DBUG_VOID_RETURN;
650 651
}

unknown's avatar
unknown committed
652

653 654
void init_parser()
{
655 656
  parser.current_line= parser.read_lines= 0;
  memset(&var_reg, 0, sizeof(var_reg));
657
}
unknown's avatar
unknown committed
658 659


660
int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname)
unknown's avatar
unknown committed
661 662
{
  MY_STAT stat_info;
663 664
  char *tmp, *res_ptr;
  char eval_file[FN_REFLEN];
unknown's avatar
unknown committed
665
  int res;
666
  uint res_len;
unknown's avatar
unknown committed
667
  int fd;
668
  DYNAMIC_STRING res_ds;
669 670
  DBUG_ENTER("dyn_string_cmp");

671 672 673 674 675 676 677 678 679
  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)))
680
    die(NullS);
681 682 683 684
  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));
685
    DBUG_PRINT("info",("result: '%s'", ds->str));
686
    DBUG_RETURN(RESULT_LENGTH_MISMATCH);
687
  }
688
  if (!(tmp = (char*) my_malloc(stat_info.st_size + 1, MYF(MY_WME))))
689
    die(NullS);
690 691

  if ((fd = my_open(eval_file, O_RDONLY, MYF(MY_WME))) < 0)
692
    die(NullS);
693
  if (my_read(fd, (byte*)tmp, stat_info.st_size, MYF(MY_WME|MY_NABP)))
694
    die(NullS);
695 696 697 698 699 700
  tmp[stat_info.st_size] = 0;
  init_dynamic_string(&res_ds, "", 0, 65536);
  if (eval_result)
  {
    do_eval(&res_ds, tmp);
    res_ptr = res_ds.str;
701
    if ((res_len = res_ds.length) != ds->length)
702
    {
703
      res= RESULT_LENGTH_MISMATCH;
704 705 706 707 708 709 710 711
      goto err;
    }
  }
  else
  {
    res_ptr = tmp;
    res_len = stat_info.st_size;
  }
unknown's avatar
unknown committed
712

713 714
  res= (memcmp(res_ptr, ds->str, res_len)) ?
    RESULT_CONTENT_MISMATCH : RESULT_OK;
unknown's avatar
unknown committed
715

716 717 718 719
err:
  if (res && eval_result)
    str_to_file(fn_format(eval_file, fname, "", ".eval",2), res_ptr,
		res_len);
unknown's avatar
unknown committed
720

721 722
  my_free((gptr) tmp, MYF(0));
  my_close(fd, MYF(MY_WME));
723
  dynstr_free(&res_ds);
unknown's avatar
unknown committed
724

725
  DBUG_RETURN(res);
unknown's avatar
unknown committed
726 727
}

728
static int check_result(DYNAMIC_STRING* ds, const char *fname,
729
			my_bool require_option)
unknown's avatar
unknown committed
730
{
731 732
  int error= RESULT_OK;
  int res= dyn_string_cmp(ds, fname);
733

734
  DBUG_ENTER("check_result");
735 736 737

  if (res && require_option)
    abort_not_supported_test();
738
  switch (res) {
739
  case RESULT_OK:
740
    break; /* ok */
741
  case RESULT_LENGTH_MISMATCH:
742
    verbose_msg("Result length mismatch");
743
    error= RESULT_LENGTH_MISMATCH;
744
    break;
745
  case RESULT_CONTENT_MISMATCH:
746
    verbose_msg("Result content mismatch");
747
    error= RESULT_CONTENT_MISMATCH;
748 749 750 751 752
    break;
  default: /* impossible */
    die("Unknown error code from dyn_string_cmp()");
  }
  if (error)
753
    reject_dump(fname, ds->str, ds->length);
754
  DBUG_RETURN(error);
unknown's avatar
unknown committed
755 756
}

757

758
VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw,
759
	     my_bool ignore_not_existing)
760 761 762
{
  int digit;
  VAR* v;
763 764 765 766
  DBUG_ENTER("var_get");
  DBUG_PRINT("enter",("var_name: %s",var_name));

  if (*var_name != '$')
767
    goto err;
768
  digit = *++var_name - '0';
unknown's avatar
unknown committed
769
  if (digit < 0 || digit >= 10)
770
  {
771
    const char *save_var_name = var_name, *end;
772
    uint length;
unknown's avatar
unknown committed
773
    end = (var_name_end) ? *var_name_end : 0;
774
    while (my_isvar(charset_info,*var_name) && var_name != end)
775
      var_name++;
776 777 778 779
    if (var_name == save_var_name)
    {
      if (ignore_not_existing)
	DBUG_RETURN(0);
unknown's avatar
unknown committed
780
      die("Empty variable");
781
    }
782
    length= (uint) (var_name - save_var_name);
783 784
    if (length >= MAX_VAR_NAME)
      die("Too long variable name: %s", save_var_name);
unknown's avatar
unknown committed
785

786
    if (!(v = (VAR*) hash_search(&var_hash, save_var_name, length)))
787
    {
788
      char buff[MAX_VAR_NAME+1];
unknown's avatar
unknown committed
789
      strmake(buff, save_var_name, length);
790
      v= var_from_env(buff, "");
791
    }
792
    var_name--;					/* Point at last character */
793
  }
794
  else
795
    v = var_reg + digit;
unknown's avatar
unknown committed
796

797 798 799 800
  if (!raw && v->int_dirty)
  {
    sprintf(v->str_val, "%d", v->int_val);
    v->int_dirty = 0;
801
    v->str_val_len = strlen(v->str_val);
802
  }
803
  if (var_name_end)
804
    *var_name_end = var_name  ;
805
  DBUG_RETURN(v);
806 807
err:
  if (var_name_end)
808 809
    *var_name_end = 0;
  die("Unsupported variable name: %s", var_name);
810
  DBUG_RETURN(0);
811 812
}

813
static VAR *var_obtain(const char *name, int len)
unknown's avatar
unknown committed
814 815
{
  VAR* v;
816
  if ((v = (VAR*)hash_search(&var_hash, name, len)))
unknown's avatar
unknown committed
817
    return v;
818
  v = var_init(0, name, len, "", 0);
unknown's avatar
SCRUM  
unknown committed
819
  my_hash_insert(&var_hash, (byte*)v);
unknown's avatar
unknown committed
820 821 822
  return v;
}

823 824
int var_set(const char *var_name, const char *var_name_end,
            const char *var_val, const char *var_val_end)
825 826 827
{
  int digit;
  VAR* v;
828 829 830 831 832 833
  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)));

834
  if (*var_name++ != '$')
835 836 837 838
  {
    var_name--;
    die("Variable name in %s does not start with '$'", var_name);
  }
839
  digit = *var_name - '0';
840
  if (!(digit < 10 && digit >= 0))
841 842 843
  {
    v = var_obtain(var_name, (uint) (var_name_end - var_name));
  }
844
  else
845
    v = var_reg + digit;
846
  DBUG_RETURN(eval_expr(v, var_val, (const char**)&var_val_end));
847 848
}

849

850
int open_file(const char *name)
unknown's avatar
unknown committed
851
{
852
  char buff[FN_REFLEN];
853 854
  DBUG_ENTER("open_file");
  DBUG_PRINT("enter", ("name: %s", name));
855 856 857 858 859 860 861
  if (!test_if_hard_path(name))
  {
    strxmov(buff, opt_basedir, name, NullS);
    name=buff;
  }
  fn_format(buff,name,"","",4);

862
  if (cur_file == file_stack_end)
unknown's avatar
unknown committed
863
    die("Source directives are nesting too deep");
864
  cur_file++;
865 866 867
  if (!(cur_file->file = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0))))
  {
    cur_file--;
868
    die("Could not open file %s", buff);
869 870
  }
  cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
871
  *++lineno=1;
872
  DBUG_RETURN(0);
873
}
874

875 876 877 878 879 880 881 882

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

int check_eol_junk(const char *eol)
{
883
  const char *p= eol;
884 885
  DBUG_ENTER("check_eol_junk");
  DBUG_PRINT("enter", ("eol: %s", eol));
886
  /* Remove all spacing chars except new line */
887
  while (*p && my_isspace(charset_info, *p) && (*p != '\n'))
888 889 890
    p++;

  /* Check for extra delimiter */
891
  if (*p && !strncmp(p, delimiter, delimiter_length))
892
    die("Extra delimiter \"%s\" found", delimiter);
893

894 895 896 897 898 899 900
  /* Allow trailing # comment */
  if (*p && *p != '#')
  {
    if (*p == '\n')
      die("Missing delimiter");
    die("End of line junk detected: \"%s\"", p);
  }
901
  DBUG_RETURN(0);
unknown's avatar
unknown committed
902 903
}

unknown's avatar
unknown committed
904

905
/* ugly long name, but we are following the convention */
906
int do_wait_for_slave_to_stop(struct st_query *q __attribute__((unused)))
907 908 909 910
{
  MYSQL* mysql = &cur_con->mysql;
  for (;;)
  {
unknown's avatar
unknown committed
911
    MYSQL_RES *res;
912 913 914
    MYSQL_ROW row;
    int done;
    LINT_INIT(res);
915

unknown's avatar
unknown committed
916 917
    if (mysql_query(mysql,"show status like 'Slave_running'") ||
	!(res=mysql_store_result(mysql)))
918 919 920 921 922 923 924 925 926 927 928
      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
929
    my_sleep(SLAVE_POLL_INTERVAL);
930 931 932 933
  }
  return 0;
}

934
int do_require_manager(struct st_query *query __attribute__((unused)) )
935 936 937 938 939 940
{
  if (!manager)
    abort_not_supported_test();
  return 0;
}

941
#ifndef EMBEDDED_LIBRARY
942
int do_server_start(struct st_query *q)
943
{
944
  return do_server_op(q, "start");
945 946
}

947
int do_server_stop(struct st_query *q)
948
{
949
  return do_server_op(q, "stop");
950 951
}

952
int do_server_op(struct st_query *q, const char *op)
953
{
954 955
  char *p= q->first_argument;
  char com_buf[256], *com_p;
956 957 958 959
  if (!manager)
  {
    die("Manager is not initialized, manager commands are not possible");
  }
960 961
  com_p= strmov(com_buf,op);
  com_p= strmov(com_p,"_exec ");
962
  if (!*p)
963 964
    die("Missing server name in server_%s", op);
  while (*p && !my_isspace(charset_info, *p))
unknown's avatar
unknown committed
965
   *com_p++= *p++;
966 967 968 969 970 971
  *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);
972 973
  while (!manager->eof)
  {
974
    if (mysql_manager_fetch_line(manager, com_buf, sizeof(com_buf)))
975 976 977 978
      die("Error fetching result line: %s(%d)", manager->last_error,
	  manager->last_errno);
  }

979
  q->last_argument= p;
980 981
  return 0;
}
982
#endif
983

984 985 986 987 988 989

/*
  Source and execute the given file

  SYNOPSIS
    do_source()
990
    query	called command
991 992 993 994 995 996 997 998

  DESCRIPTION
    source <file_name>

    Open the file <file_name> and execute it

*/

999
int do_source(struct st_query *query)
unknown's avatar
unknown committed
1000
{
1001
  char *p= query->first_argument, *name;
1002
  if (!*p)
1003
    die("Missing file name in source");
1004
  name= p;
1005
  while (*p && !my_isspace(charset_info,*p))
unknown's avatar
unknown committed
1006
    p++;
1007 1008
  if (*p)
    *p++= 0;
1009 1010 1011 1012 1013
  query->last_argument= p;
  /*
     If this file has already been sourced, dont source it again.
     It's already available in the q_lines cache
  */
1014 1015
  if (parser.current_line < (parser.read_lines - 1))
    return 0;
unknown's avatar
unknown committed
1016 1017 1018
  return open_file(name);
}

1019

1020 1021 1022 1023 1024
/*
  Execute given command.

  SYNOPSIS
    do_exec()
1025
    query	called command
1026 1027

  DESCRIPTION
1028 1029 1030 1031 1032 1033
    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.
1034 1035 1036

*/

1037
static void do_exec(struct st_query *query)
1038
{
unknown's avatar
unknown committed
1039
  int error;
1040
  DYNAMIC_STRING *ds= NULL;
1041 1042 1043
  DYNAMIC_STRING ds_tmp;
  char buf[1024];
  FILE *res_file;
1044
  char *cmd= query->first_argument;
unknown's avatar
unknown committed
1045
  DBUG_ENTER("do_exec");
1046

unknown's avatar
unknown committed
1047
  while (*cmd && my_isspace(charset_info, *cmd))
1048 1049
    cmd++;
  if (!*cmd)
1050
    die("Missing argument in exec");
1051
  query->last_argument= query->end;
1052

unknown's avatar
unknown committed
1053 1054
  DBUG_PRINT("info", ("Executing '%s'", cmd));

1055
  if (!(res_file= popen(cmd, "r")) && query->abort_on_error)
1056
    die("popen(\"%s\", \"r\") failed", cmd);
unknown's avatar
unknown committed
1057

1058
  if (disable_result_log)
1059
  {
unknown's avatar
unknown committed
1060
    while (fgets(buf, sizeof(buf), res_file))
1061 1062 1063 1064
    {
      buf[strlen(buf)-1]=0;
      DBUG_PRINT("exec_result",("%s", buf));
    }
1065 1066
  }
  else
1067
  {
1068
    if (query->record_file[0])
1069 1070 1071 1072 1073 1074
    {
      init_dynamic_string(&ds_tmp, "", 16384, 65536);
      ds= &ds_tmp;
    }
    else
      ds= &ds_res;
1075

1076
    while (fgets(buf, sizeof(buf), res_file))
1077
      replace_dynstr_append(ds, buf);
unknown's avatar
unknown committed
1078 1079 1080
  }
  error= pclose(res_file);
  if (error != 0)
unknown's avatar
patch  
unknown committed
1081
  {
unknown's avatar
unknown committed
1082 1083 1084
    uint status= WEXITSTATUS(error), i;
    my_bool ok= 0;

1085
    if (query->abort_on_error)
1086
      die("command \"%s\" failed", cmd);
unknown's avatar
unknown committed
1087 1088 1089

    DBUG_PRINT("info",
               ("error: %d, status: %d", error, status));
1090
    for (i= 0; i < query->expected_errors; i++)
unknown's avatar
patch  
unknown committed
1091
    {
1092 1093
      DBUG_PRINT("info",
                 ("error: %d, status: %d", error, status));
unknown's avatar
unknown committed
1094
      DBUG_PRINT("info", ("expected error: %d",
1095 1096 1097
                          query->expected_errno[i].code.errnum));
      if ((query->expected_errno[i].type == ERR_ERRNO) &&
          (query->expected_errno[i].code.errnum == status))
1098
      {
unknown's avatar
unknown committed
1099
        ok= 1;
1100 1101 1102
        verbose_msg("command \"%s\" failed with expected error: %d",
                    cmd, status);
      }
unknown's avatar
patch  
unknown committed
1103
    }
unknown's avatar
unknown committed
1104
    if (!ok)
1105 1106
      die("command \"%s\" failed with wrong error: %d",
          cmd, status);
unknown's avatar
patch  
unknown committed
1107
  }
1108 1109
  else if (query->expected_errno[0].type == ERR_ERRNO &&
           query->expected_errno[0].code.errnum != 0)
unknown's avatar
patch  
unknown committed
1110 1111
  {
    /* Error code we wanted was != 0, i.e. not an expected success */
1112
    die("command \"%s\" succeeded - should have failed with errno %d...",
1113
        cmd, query->expected_errno[0].code.errnum);
unknown's avatar
patch  
unknown committed
1114
  }
unknown's avatar
unknown committed
1115

unknown's avatar
unknown committed
1116 1117
  if (!disable_result_log)
  {
1118 1119
    if (glob_replace)
      free_replace();
1120

1121 1122
    if (record)
    {
1123
      if (!query->record_file[0] && !result_file)
1124
        die("Missing result file");
1125
      if (!result_file)
1126
        str_to_file(query->record_file, ds->str, ds->length);
1127
    }
1128
    else if (query->record_file[0])
1129
    {
1130
      error= check_result(ds, query->record_file, query->require_file);
1131 1132 1133
    }
    if (ds == &ds_tmp)
      dynstr_free(&ds_tmp);
1134 1135 1136
  }
}

unknown's avatar
unknown committed
1137

1138
int var_query_set(VAR* v, const char *p, const char** p_end)
1139 1140 1141 1142 1143 1144
{
  char* end = (char*)((p_end && *p_end) ? *p_end : p + strlen(p));
  MYSQL_RES *res;
  MYSQL_ROW row;
  MYSQL* mysql = &cur_con->mysql;
  LINT_INIT(res);
unknown's avatar
unknown committed
1145

1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159
  while (end > p && *end != '`')
    --end;
  if (p == end)
    die("Syntax error in query, missing '`'");
  ++p;

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

  if ((row = mysql_fetch_row(res)) && row[0])
1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181
  {
    /*
      Concatenate all row results with tab in between to allow us to work
      with results from many columns (for example from SHOW VARIABLES)
    */
    DYNAMIC_STRING result;
    uint i;
    ulong *lengths;
    char *end;

    init_dynamic_string(&result, "", 16384, 65536);
    lengths= mysql_fetch_lengths(res);
    for (i=0; i < mysql_num_fields(res); i++)
    {
      if (row[0])
	dynstr_append_mem(&result, row[i], lengths[i]);
      dynstr_append_mem(&result, "\t", 1);
    }
    end= result.str + result.length-1;
    eval_expr(v, result.str, (const char**) &end);
    dynstr_free(&result);
  }
1182 1183 1184 1185 1186 1187
  else
    eval_expr(v, "", 0);

  mysql_free_result(res);
  return 0;
}
1188

1189
void var_copy(VAR *dest, VAR *src)
unknown's avatar
unknown committed
1190
{
1191 1192 1193 1194
  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
1195
  if (dest->alloced_len < src->alloced_len &&
1196 1197 1198
      !(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
1199
    die("Out of memory");
1200 1201 1202 1203 1204 1205 1206
  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
1207 1208
}

1209
int eval_expr(VAR* v, const char *p, const char** p_end)
1210 1211
{
  VAR* vp;
1212
  if (*p == '$')
1213 1214
  {
    if ((vp = var_get(p,p_end,0,0)))
1215
    {
1216 1217
      var_copy(v, vp);
      return 0;
unknown's avatar
unknown committed
1218
    }
1219
  }
1220
  else if (*p == '`')
1221 1222 1223
  {
    return var_query_set(v, p, p_end);
  }
unknown's avatar
unknown committed
1224 1225
  else
    {
1226 1227 1228 1229
      int new_val_len = (p_end && *p_end) ?
	 (int) (*p_end - p) : (int) strlen(p);
      if (new_val_len + 1 >= v->alloced_len)
      {
1230
	v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
1231 1232
	  MIN_VAR_ALLOC : new_val_len + 1;
	if (!(v->str_val =
1233
	      v->str_val ? my_realloc(v->str_val, v->alloced_len+1,
1234
				      MYF(MY_WME)) :
1235
	      my_malloc(v->alloced_len+1, MYF(MY_WME))))
1236 1237 1238 1239 1240
	  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
1241 1242
      v->int_val=atoi(p);
      v->int_dirty=0;
1243 1244
      return 0;
    }
1245

1246 1247 1248 1249
  die("Invalid expr: %s", p);
  return 1;
}

1250

1251
enum enum_operator
1252
{
1253 1254 1255
  DO_DEC,
  DO_INC
};
1256

1257
/*
1258
  Decrease or increase the value of a variable
1259 1260

  SYNOPSIS
1261 1262 1263 1264
    do_modify_var()
    query	called command
    name        human readable name of operator
    operator    operation to perform on the var
1265 1266 1267

  DESCRIPTION
    dec $var_name
1268
    inc $var_name
1269

1270 1271
*/

1272 1273
int do_modify_var(struct st_query *query, const char *name,
                  enum enum_operator operator)
1274
{
1275
  const char *p= query->first_argument;
1276
  VAR* v;
1277
  if (!*p)
1278
    die("Missing arguments to %s", name);
1279
  if (*p != '$')
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293
    die("First argument to %s must be a variable (start with $)", name);
  v= var_get(p, &p, 1, 0);
  switch (operator){
  case DO_DEC:
    v->int_val--;
    break;
  case DO_INC:
    v->int_val++;
    break;
  default:
    die("Invalid operator to do_operator");
    break;
  }
  v->int_dirty= 1;
1294
  query->last_argument= (char*)++p;
1295 1296 1297
  return 0;
}

1298

1299
int do_system(struct st_query *q)
unknown's avatar
unknown committed
1300
{
1301
  char *p=q->first_argument;
unknown's avatar
unknown committed
1302
  VAR v;
1303
  var_init(&v, 0, 0, 0, 0);
unknown's avatar
unknown committed
1304
  eval_expr(&v, p, 0); /* NULL terminated */
unknown's avatar
unknown committed
1305
  if (v.str_val_len)
unknown's avatar
unknown committed
1306
  {
unknown's avatar
unknown committed
1307
    char expr_buf[1024];
unknown's avatar
unknown committed
1308 1309 1310 1311 1312
    if ((uint)v.str_val_len > sizeof(expr_buf) - 1)
      v.str_val_len = sizeof(expr_buf) - 1;
    memcpy(expr_buf, v.str_val, v.str_val_len);
    expr_buf[v.str_val_len] = 0;
    DBUG_PRINT("info", ("running system command '%s'", expr_buf));
1313 1314 1315 1316 1317 1318 1319
    if (system(expr_buf))
    {
      if (q->abort_on_error)
        die("system command '%s' failed", expr_buf);
      /* If ! abort_on_error, display message and continue */
      verbose_msg("system command '%s' failed", expr_buf);
    }
unknown's avatar
unknown committed
1320
  }
1321 1322
  else
    die("Missing arguments to system, nothing to do!");
unknown's avatar
unknown committed
1323
  var_free(&v);
1324
  q->last_argument= q->end;
unknown's avatar
unknown committed
1325 1326
  return 0;
}
1327

1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347

/*
  Print the content between echo and <delimiter> to result file.
  If content is a variable, the variable value will be retrieved

  SYNOPSIS
    do_echo()
    q  called command

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

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

*/

1348
int do_echo(struct st_query *q)
1349
{
1350 1351
  char *p= q->first_argument;
  DYNAMIC_STRING *ds;
1352
  DYNAMIC_STRING ds_tmp;
1353
  VAR v;
1354
  var_init(&v,0,0,0,0);
1355 1356

  if (q->record_file[0])
unknown's avatar
unknown committed
1357
  {
1358
    init_dynamic_string(&ds_tmp, "", 256, 512);
1359
    ds= &ds_tmp;
unknown's avatar
unknown committed
1360
  }
1361 1362 1363 1364 1365 1366 1367
  else
    ds= &ds_res;

  eval_expr(&v, p, 0); /* NULL terminated */
  if (v.str_val_len)
    dynstr_append_mem(ds, v.str_val, v.str_val_len);
  dynstr_append_mem(ds, "\n", 1);
unknown's avatar
unknown committed
1368
  var_free(&v);
1369 1370
  if (ds == &ds_tmp)
    dynstr_free(&ds_tmp);
1371
  q->last_argument= q->end;
1372 1373 1374
  return 0;
}

1375

1376
int do_sync_with_master2(long offset)
unknown's avatar
unknown committed
1377 1378 1379
{
  MYSQL_RES* res;
  MYSQL_ROW row;
1380
  MYSQL* mysql= &cur_con->mysql;
unknown's avatar
unknown committed
1381
  char query_buf[FN_REFLEN+128];
1382
  int tries= 0;
1383 1384
  int rpl_parse;

1385 1386 1387 1388
  if (!master_pos.file[0])
  {
    die("Line %u: Calling 'sync_with_master' without calling 'save_master_pos'", start_lineno);
  }
1389
  rpl_parse= mysql_rpl_parse_enabled(mysql);
1390
  mysql_disable_rpl_parse(mysql);
unknown's avatar
unknown committed
1391

unknown's avatar
unknown committed
1392
  sprintf(query_buf, "select master_pos_wait('%s', %ld)", master_pos.file,
1393
	  master_pos.pos + offset);
1394 1395 1396

wait_for_position:

1397 1398
  if (mysql_query(mysql, query_buf))
    die("line %u: failed in %s: %d: %s", start_lineno, query_buf,
unknown's avatar
unknown committed
1399 1400
	mysql_errno(mysql), mysql_error(mysql));

1401
  if (!(last_result= res= mysql_store_result(mysql)))
1402 1403
    die("line %u: mysql_store_result() returned NULL for '%s'", start_lineno,
	query_buf);
1404
  if (!(row= mysql_fetch_row(res)))
unknown's avatar
unknown committed
1405
    die("line %u: empty result in %s", start_lineno, query_buf);
1406
  if (!row[0])
1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420
  {
    /*
      It may be that the slave SQL thread has not started yet, though START
      SLAVE has been issued ?
    */
    if (tries++ == 3)
    {
      die("line %u: could not sync with master ('%s' returned NULL)", 
          start_lineno, query_buf);
    }
    sleep(1); /* So at most we will wait 3 seconds and make 4 tries */
    mysql_free_result(res);
    goto wait_for_position;
  }
unknown's avatar
unknown committed
1421 1422
  mysql_free_result(res);
  last_result=0;
1423
  if (rpl_parse)
1424
    mysql_enable_rpl_parse(mysql);
unknown's avatar
unknown committed
1425

unknown's avatar
unknown committed
1426 1427 1428
  return 0;
}

1429
int do_sync_with_master(struct st_query *query)
1430
{
1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443
  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);
1444
}
1445

unknown's avatar
unknown committed
1446 1447 1448 1449 1450
int do_save_master_pos()
{
  MYSQL_RES* res;
  MYSQL_ROW row;
  MYSQL* mysql = &cur_con->mysql;
1451
  const char *query;
1452 1453 1454 1455
  int rpl_parse;

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

1457
  if (mysql_query(mysql, query= "show master status"))
1458
    die("failed in show master status: %d: %s",
unknown's avatar
unknown committed
1459 1460
	mysql_errno(mysql), mysql_error(mysql));

1461
  if (!(last_result =res = mysql_store_result(mysql)))
1462 1463
    die("line %u: mysql_store_result() retuned NULL for '%s'", start_lineno,
	query);
1464
  if (!(row = mysql_fetch_row(res)))
unknown's avatar
unknown committed
1465
    die("line %u: empty result in show master status", start_lineno);
1466 1467
  strnmov(master_pos.file, row[0], sizeof(master_pos.file)-1);
  master_pos.pos = strtoul(row[1], (char**) 0, 10);
1468
  mysql_free_result(res); last_result=0;
unknown's avatar
unknown committed
1469

1470
  if (rpl_parse)
1471
    mysql_enable_rpl_parse(mysql);
unknown's avatar
unknown committed
1472

unknown's avatar
unknown committed
1473 1474 1475 1476
  return 0;
}


1477 1478 1479 1480 1481
/*
  Assign the variable <var_name> with <var_val>

  SYNOPSIS
   do_let()
1482
    query	called command
1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495

  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
*/

1496
int do_let(struct st_query *query)
1497
{
1498
  char *p= query->first_argument;
1499
  char *var_name, *var_name_end, *var_val_start;
1500 1501

  /* Find <var_name> */
1502
  if (!*p)
1503
    die("Missing arguments to let");
1504 1505
  var_name= p;
  while (*p && (*p != '=') && !my_isspace(charset_info,*p))
1506
    p++;
1507 1508
  var_name_end= p;
  if (var_name+1 == var_name_end)
1509
    die("Missing variable name in let");
1510
  while (my_isspace(charset_info,*p))
1511
    p++;
1512
  if (*p++ != '=')
1513 1514 1515
    die("Missing assignment operator in let");

  /* Find start of <var_val> */
1516
  while (*p && my_isspace(charset_info,*p))
1517
    p++;
1518 1519
  var_val_start= p;
  query->last_argument= query->end;
1520
  /* Assign var_val to var_name */
1521
  return var_set(var_name, var_name_end, var_val_start, query->end);
1522 1523
}

1524 1525 1526 1527 1528 1529 1530 1531

/*
  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)
1532
{
1533 1534 1535 1536
  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);
1537 1538
}

1539

1540
int do_rpl_probe(struct st_query *query __attribute__((unused)))
1541
{
unknown's avatar
unknown committed
1542
  DBUG_ENTER("do_rpl_probe");
1543
  if (mysql_rpl_probe(&cur_con->mysql))
unknown's avatar
unknown committed
1544 1545
    die("Failed in mysql_rpl_probe(): '%s'", mysql_error(&cur_con->mysql));
  DBUG_RETURN(0);
1546 1547
}

1548

1549
int do_enable_rpl_parse(struct st_query *query __attribute__((unused)))
1550 1551 1552 1553 1554
{
  mysql_enable_rpl_parse(&cur_con->mysql);
  return 0;
}

1555

1556
int do_disable_rpl_parse(struct st_query *query __attribute__((unused)))
1557 1558 1559 1560 1561 1562
{
  mysql_disable_rpl_parse(&cur_con->mysql);
  return 0;
}


1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576
/*
  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

  DESCRIPTION
    sleep <seconds>
    real_sleep

*/

1577
int do_sleep(struct st_query *query, my_bool real_sleep)
unknown's avatar
unknown committed
1578
{
1579 1580 1581 1582 1583 1584
  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
1585
    p++;
1586
  if (!*p)
1587
    die("Missing argument to sleep");
1588 1589 1590 1591 1592 1593 1594 1595 1596
  sleep_start= p;
  /* Check that arg starts with a digit, not handled by my_strtod */
  if (!my_isdigit(charset_info, *sleep_start))
    die("Invalid argument to sleep \"%s\"", query->first_argument);
  sleep_val= my_strtod(sleep_start, &sleep_end, &error);
  if (error)
    die("Invalid argument to sleep \"%s\"", query->first_argument);

  /* Fixed sleep time selected by --sleep option */
1597
  if (opt_sleep && !real_sleep)
1598 1599 1600 1601
    sleep_val= opt_sleep;

  my_sleep((ulong) (sleep_val * 1000000L));
  query->last_argument= sleep_end;
unknown's avatar
unknown committed
1602
  return 0;
unknown's avatar
unknown committed
1603 1604
}

1605
static void get_file_name(char *filename, struct st_query *q)
1606
{
1607
  char *p= q->first_argument, *name;
1608 1609
  if (!*p)
    die("Missing file name argument");
1610
  name= p;
1611 1612 1613 1614
  while (*p && !my_isspace(charset_info,*p))
    p++;
  if (*p)
    *p++= 0;
1615
  q->last_argument= p;
1616
  strmake(filename, name, FN_REFLEN);
1617 1618
}

1619
static void set_charset(struct st_query *q)
unknown's avatar
unknown committed
1620
{
1621 1622
  char *charset_name= q->first_argument;
  char *p;
unknown's avatar
unknown committed
1623 1624

  if (!charset_name || !*charset_name)
1625
    die("Missing charset name in 'character_set'");
unknown's avatar
unknown committed
1626
  /* Remove end space */
1627 1628 1629 1630 1631
  p= charset_name;
  while (*p && !my_isspace(charset_info,*p))
    p++;
  if(*p)
    *p++= 0;
1632
  q->last_argument= p;
unknown's avatar
unknown committed
1633 1634 1635 1636
  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
1637

1638
static uint get_errcodes(match_err *to,struct st_query *q)
unknown's avatar
unknown committed
1639
{
1640
  char *p= q->first_argument;
1641
  uint count= 0;
1642

1643
  DBUG_ENTER("get_errcodes");
1644

unknown's avatar
unknown committed
1645
  if (!*p)
1646
    die("Missing argument in %s", q->query);
1647

1648
  do
1649
  {
1650 1651 1652
    if (*p == 'S')
    {
      /* SQLSTATE string */
1653 1654 1655 1656 1657 1658 1659
      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;

1660 1661
      to[count].type= ERR_SQLSTATE;
    }
1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679
    else if (*p == 'E')
    {
      /* SQL error as string */
      st_error *e= global_error;
      char *start= p++;
      
      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)
1680
	die("Unknown SQL error '%s'", start);
1681
    }
1682 1683 1684
    else
    {
      long val;
1685 1686

      if (!(p= str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val)))
1687
	die("Invalid argument in %s", q->query);
1688 1689 1690
      to[count].code.errnum= (uint) val;
      to[count].type= ERR_ERRNO;
    }
unknown's avatar
unknown committed
1691
    count++;
1692
  } while (*(p++) == ',');
1693
  q->last_argument= (p - 1);
1694
  to[count].type= ERR_EMPTY;                        /* End of data */
unknown's avatar
unknown committed
1695
  DBUG_RETURN(count);
unknown's avatar
unknown committed
1696 1697
}

unknown's avatar
unknown committed
1698 1699 1700
/*
  Get a string;  Return ptr to end of string
  Strings may be surrounded by " or '
1701 1702

  If string is a '$variable', return the value of the variable.
unknown's avatar
unknown committed
1703 1704 1705
*/


1706
static char *get_string(char **to_ptr, char **from_ptr,
1707
			struct st_query *q)
unknown's avatar
unknown committed
1708 1709
{
  reg1 char c,sep;
1710
  char *to= *to_ptr, *from= *from_ptr, *start=to;
unknown's avatar
unknown committed
1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754
  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)
1755
    die("Wrong string argument in %s", q->query);
unknown's avatar
unknown committed
1756

1757
  while (my_isspace(charset_info,*from))	/* Point to next string */
unknown's avatar
unknown committed
1758 1759
    from++;

1760 1761
  *to =0;				/* End of string marker */
  *to_ptr= to+1;			/* Store pointer to end */
unknown's avatar
unknown committed
1762
  *from_ptr= from;
1763 1764 1765 1766 1767 1768 1769 1770

  /* 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)
    {
1771
      DBUG_PRINT("info",("var: '%s' -> '%s'", start, var->str_val));
1772 1773 1774 1775
      DBUG_RETURN(var->str_val);	/* return found variable value */
    }
  }
  DBUG_RETURN(start);
unknown's avatar
unknown committed
1776 1777 1778 1779 1780 1781 1782
}


/*
  Get arguments for replace. The syntax is:
  replace from to [from to ...]
  Where each argument may be quoted with ' or "
1783 1784
  A argument may also be a variable, in which case the value of the
  variable is replaced.
unknown's avatar
unknown committed
1785 1786 1787 1788 1789
*/

static void get_replace(struct st_query *q)
{
  uint i;
1790
  char *from= q->first_argument;
1791
  char *buff,*start;
unknown's avatar
unknown committed
1792 1793 1794 1795
  char word_end_chars[256],*pos;
  POINTER_ARRAY to_array,from_array;
  DBUG_ENTER("get_replace");

unknown's avatar
unknown committed
1796
  free_replace();
1797

unknown's avatar
unknown committed
1798 1799 1800
  bzero((char*) &to_array,sizeof(to_array));
  bzero((char*) &from_array,sizeof(from_array));
  if (!*from)
1801
    die("Missing argument in %s", q->query);
1802
  start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
unknown's avatar
unknown committed
1803 1804 1805
  while (*from)
  {
    char *to=buff;
1806
    to=get_string(&buff, &from, q);
unknown's avatar
unknown committed
1807
    if (!*from)
1808
      die("Wrong number of arguments to replace_result in '%s'", q->query);
unknown's avatar
unknown committed
1809
    insert_pointer_name(&from_array,to);
1810
    to=get_string(&buff, &from, q);
unknown's avatar
unknown committed
1811 1812 1813
    insert_pointer_name(&to_array,to);
  }
  for (i=1,pos=word_end_chars ; i < 256 ; i++)
1814
    if (my_isspace(charset_info,i))
unknown's avatar
unknown committed
1815
      *pos++= i;
1816
  *pos=0;					/* End pointer */
unknown's avatar
unknown committed
1817 1818 1819 1820 1821
  if (!(glob_replace=init_replace((char**) from_array.typelib.type_names,
				  (char**) to_array.typelib.type_names,
				  (uint) from_array.typelib.count,
				  word_end_chars)) ||
      initialize_replace_buffer())
1822
    die("Can't initialize replace from '%s'", q->query);
unknown's avatar
unknown committed
1823 1824
  free_pointer_array(&from_array);
  free_pointer_array(&to_array);
1825
  my_free(start, MYF(0));
1826
  q->last_argument= q->end;
1827
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1828 1829 1830 1831
}

void free_replace()
{
1832
  DBUG_ENTER("free_replace");
unknown's avatar
unknown committed
1833 1834 1835 1836 1837 1838
  if (glob_replace)
  {
    my_free((char*) glob_replace,MYF(0));
    glob_replace=0;
    free_replace_buffer();
  }
1839
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1840 1841
}

1842 1843

int select_connection_name(const char *name)
unknown's avatar
unknown committed
1844 1845
{
  struct connection *con;
1846 1847
  DBUG_ENTER("select_connection2");
  DBUG_PRINT("enter",("name: '%s'", name));
1848

1849
  for (con= cons; con < next_con; con++)
1850
  {
1851
    if (!strcmp(con->name, name))
1852
    {
1853
      cur_con= con;
1854 1855 1856
      DBUG_RETURN(0);
    }
  }
unknown's avatar
unknown committed
1857
  die("connection '%s' not found in connection pool", name);
1858
  DBUG_RETURN(1);				/* Never reached */
unknown's avatar
unknown committed
1859 1860
}

1861 1862

int select_connection(struct st_query *query)
1863
{
1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880
  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)
1881
{
1882
  char *p= q->first_argument, *name;
1883
  struct connection *con;
1884 1885 1886
  DBUG_ENTER("close_connection");
  DBUG_PRINT("enter",("name: '%s'",p));

1887
  if (!*p)
1888
    die("Missing connection name in connect");
1889
  name= p;
1890
  while (*p && !my_isspace(charset_info,*p))
1891
    p++;
1892

1893 1894
  if (*p)
    *p++= 0;
1895 1896
  q->last_argument= p;
  for (con= cons; con < next_con; con++)
1897
  {
1898
    if (!strcmp(con->name, name))
1899
    {
1900
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
1901 1902 1903
      if (q->type == Q_DIRTY_CLOSE)
      {
	if (con->mysql.net.vio)
unknown's avatar
unknown committed
1904
	{
unknown's avatar
unknown committed
1905 1906
	  vio_delete(con->mysql.net.vio);
	  con->mysql.net.vio = 0;
unknown's avatar
unknown committed
1907
	}
unknown's avatar
unknown committed
1908
      }
1909
#endif
1910 1911 1912 1913
      mysql_close(&con->mysql);
      DBUG_RETURN(0);
    }
  }
1914
  die("connection '%s' not found in connection pool", name);
1915
  DBUG_RETURN(1);				/* Never reached */
1916 1917
}

unknown's avatar
unknown committed
1918

1919 1920
/*
   This one now is a hack - we may want to improve in in the
unknown's avatar
unknown committed
1921 1922 1923
   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
1924
*/
1925

1926
char* safe_get_param(char *str, char** arg, const char *msg)
unknown's avatar
unknown committed
1927
{
1928
  DBUG_ENTER("safe_get_param");
1929
  while (*str && my_isspace(charset_info,*str))
unknown's avatar
unknown committed
1930
    str++;
1931
  *arg= str;
unknown's avatar
unknown committed
1932 1933
  for (; *str && *str != ',' && *str != ')' ; str++)
  {
1934
    if (my_isspace(charset_info,*str))
1935
      *str= 0;
unknown's avatar
unknown committed
1936
  }
1937
  if (!*str)
unknown's avatar
unknown committed
1938
    die(msg);
1939

1940
  *str++= 0;
1941
  DBUG_RETURN(str);
unknown's avatar
unknown committed
1942 1943
}

1944
#ifndef EMBEDDED_LIBRARY
1945 1946 1947 1948 1949 1950 1951 1952 1953 1954
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);

}
1955
#endif
1956

1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979

/*
  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
    This function will try to connect to the given server MAX_CON_TRIES
    times and sleep CON_RETRY_SLEEP seconds between attempts before
    finally giving up. This helps in situation when the client starts
    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
*/

1980 1981 1982
int safe_connect(MYSQL* con, const char *host, const char *user,
		 const char *pass,
		 const char *db, int port, const char *sock)
unknown's avatar
unknown committed
1983
{
1984 1985
  int con_error= 1;
  my_bool reconnect= 1;
unknown's avatar
unknown committed
1986
  int i;
1987
  for (i= 0; i < MAX_CON_TRIES; ++i)
unknown's avatar
unknown committed
1988
  {
1989
    if (mysql_real_connect(con, host,user, pass, db, port, sock,
1990
			   CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS))
unknown's avatar
unknown committed
1991
    {
1992
      con_error= 0;
unknown's avatar
unknown committed
1993 1994 1995 1996
      break;
    }
    sleep(CON_RETRY_SLEEP);
  }
1997 1998 1999 2000
  /*
   TODO: change this to 0 in future versions, but the 'kill' test relies on
   existing behavior
  */
2001
  mysql_options(con, MYSQL_OPT_RECONNECT, (char *)&reconnect);
unknown's avatar
unknown committed
2002 2003 2004
  return con_error;
}

2005

2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034
/*
  Connect to a server and handle connection errors in case when they occur.

  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)
{
  DYNAMIC_STRING ds_tmp, *ds;
2035
  my_bool reconnect= 1;
2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080
  int error= 0;

  /*
    Altough we ignore --require or --result before connect() command we still
    need to handle record_file because of "@result_file sql-command" syntax.
  */
  if (q->record_file[0])
  {
    init_dynamic_string(&ds_tmp, "", 16384, 65536);
    ds= &ds_tmp;
  }
  else
    ds= &ds_res;

  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))
  {
2081 2082
    error= handle_error("connect", q, mysql_errno(con), mysql_error(con),
                        mysql_sqlstate(con), ds);
2083 2084 2085
    *create_conn= 0;
    goto err;
  }
2086
  else if (handle_no_error(q))
2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097
  {
    /*
      Fail if there was no error but we expected it.
      We also don't want to have connection in this case.
    */
    mysql_close(con);
    *create_conn= 0;
    error= 1;
    goto err;
  }

2098 2099 2100 2101
  /*
   TODO: change this to 0 in future versions, but the 'kill' test relies on
   existing behavior
  */
2102
  mysql_options(con, MYSQL_OPT_RECONNECT, (char *)&reconnect);
2103

2104 2105 2106
  if (record)
  {
    if (!q->record_file[0] && !result_file)
2107
      die("Missing result file");
2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121
    if (!result_file)
      str_to_file(q->record_file, ds->str, ds->length);
  }
  else if (q->record_file[0])
    error|= check_result(ds, q->record_file, q->require_file);

err:
  free_replace();
  if (ds == &ds_tmp)
    dynstr_free(&ds_tmp);
  return error;
}


2122
int do_connect(struct st_query *q)
unknown's avatar
unknown committed
2123
{
2124
  char *con_name, *con_user,*con_pass, *con_host, *con_port_str,
unknown's avatar
unknown committed
2125
    *con_db, *con_sock;
2126
  char *p= q->first_argument;
2127
  char buff[FN_REFLEN];
unknown's avatar
unknown committed
2128
  int con_port;
2129
  int free_con_sock= 0;
2130 2131
  int error= 0;
  int create_conn= 1;
unknown's avatar
unknown committed
2132

2133 2134
  DBUG_ENTER("do_connect");
  DBUG_PRINT("enter",("connect: %s",p));
unknown's avatar
unknown committed
2135

2136
  if (*p != '(')
2137
    die("Syntax error in connect - expected '(' found '%c'", *p);
unknown's avatar
unknown committed
2138
  p++;
2139 2140 2141 2142 2143
  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
2144 2145
  if (!*p || *p == ';')				/* Default port and sock */
  {
2146 2147
    con_port= port;
    con_sock= (char*) unix_sock;
unknown's avatar
unknown committed
2148 2149 2150
  }
  else
  {
2151
    VAR* var_port, *var_sock;
2152
    p= safe_get_param(p, &con_port_str, "missing connection port");
2153 2154
    if (*con_port_str == '$')
    {
2155
      if (!(var_port= var_get(con_port_str, 0, 0, 0)))
2156
	die("Unknown variable '%s'", con_port_str+1);
2157
      con_port= var_port->int_val;
2158 2159
    }
    else
2160 2161
      con_port= atoi(con_port_str);
    p= safe_get_param(p, &con_sock, "missing connection socket");
2162 2163
    if (*con_sock == '$')
    {
2164
      if (!(var_sock= var_get(con_sock, 0, 0, 0)))
2165
	die("Unknown variable '%s'", con_sock+1);
2166
      if (!(con_sock= (char*)my_malloc(var_sock->str_val_len+1, MYF(0))))
2167
	die("Out of memory");
2168
      free_con_sock= 1;
2169
      memcpy(con_sock, var_sock->str_val, var_sock->str_val_len);
2170
      con_sock[var_sock->str_val_len]= 0;
2171
    }
unknown's avatar
unknown committed
2172
  }
2173
  q->last_argument= p;
unknown's avatar
unknown committed
2174

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

2178
  if (!mysql_init(&next_con->mysql))
unknown's avatar
unknown committed
2179
    die("Failed on mysql_init()");
unknown's avatar
unknown committed
2180 2181
  if (opt_compress)
    mysql_options(&next_con->mysql,MYSQL_OPT_COMPRESS,NullS);
2182
  mysql_options(&next_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
unknown's avatar
unknown committed
2183
  mysql_options(&next_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name);
2184

unknown's avatar
unknown committed
2185 2186 2187 2188 2189
#ifdef HAVE_OPENSSL
  if (opt_use_ssl)
    mysql_ssl_set(&next_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
		  opt_ssl_capath, opt_ssl_cipher);
#endif
2190
  if (con_sock && !free_con_sock && *con_sock && *con_sock != FN_LIBCHAR)
2191
    con_sock=fn_format(buff, con_sock, TMPDIR, "",0);
unknown's avatar
unknown committed
2192
  if (!con_db[0])
2193
    con_db= db;
2194
  /* Special database to allow one to connect without a database name */
unknown's avatar
unknown committed
2195
  if (con_db && !strcmp(con_db,"*NO-ONE*"))
2196
    con_db= 0;
2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214
  if (q->abort_on_error)
  {
    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': %s", con_name,
          mysql_error(&next_con->mysql));
  }
  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++;
  }
2215 2216
  if (free_con_sock)
    my_free(con_sock, MYF(MY_WME));
2217
  DBUG_RETURN(error);
unknown's avatar
unknown committed
2218 2219
}

2220

2221
int do_done(struct st_query *q)
2222
{
unknown's avatar
unknown committed
2223
  /* Check if empty block stack */
2224
  if (cur_block == block_stack)
2225 2226 2227
  {
    if (*q->query != '}')
      die("Stray 'end' command - end of block before beginning");
2228
    die("Stray '}' - end of block before beginning");
2229
  }
unknown's avatar
unknown committed
2230 2231 2232

  /* Test if inner block has been executed */
  if (cur_block->ok && cur_block->cmd == cmd_while)
2233
  {
unknown's avatar
unknown committed
2234 2235 2236
    /* Pop block from stack, re-execute outer block */
    cur_block--;
    parser.current_line = cur_block->line;
2237
  }
2238
  else
unknown's avatar
unknown committed
2239
  {
unknown's avatar
unknown committed
2240 2241 2242
    /* Pop block from stack, goto next line */
    cur_block--;
    parser.current_line++;
unknown's avatar
unknown committed
2243
  }
2244 2245 2246
  return 0;
}

unknown's avatar
unknown committed
2247 2248

int do_block(enum block_cmd cmd, struct st_query* q)
2249
{
2250 2251
  char *p= q->first_argument;
  const char *expr_start, *expr_end;
2252
  VAR v;
2253
  const char *cmd_name= (cmd == cmd_while ? "while" : "if");
unknown's avatar
unknown committed
2254 2255

  /* Check stack overflow */
2256
  if (cur_block == block_stack_end)
2257
    die("Nesting too deeply");
unknown's avatar
unknown committed
2258 2259 2260 2261 2262 2263

  /* 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)
2264
  {
unknown's avatar
unknown committed
2265 2266 2267 2268
    /* Inner block should be ignored too */
    cur_block++;
    cur_block->cmd= cmd;
    cur_block->ok= FALSE;
2269 2270
    return 0;
  }
unknown's avatar
unknown committed
2271

unknown's avatar
unknown committed
2272
  /* Parse and evaluate test expression */
2273
  expr_start= strchr(p, '(');
2274
  if (!expr_start)
2275 2276
    die("missing '(' in %s", cmd_name);
  expr_end= strrchr(expr_start, ')');
2277
  if (!expr_end)
2278
    die("missing ')' in %s", cmd_name);
2279 2280 2281 2282
  p= (char*)expr_end+1;

  while (*p && my_isspace(charset_info, *p))
    p++;
2283 2284
  if (*p == '{')
    die("Missing newline between %s and '{'", cmd_name);
2285
  if (*p)
2286
    die("Missing '{' after %s. Found \"%s\"", cmd_name, p);
2287

unknown's avatar
unknown committed
2288
  var_init(&v,0,0,0,0);
2289
  eval_expr(&v, ++expr_start, &expr_end);
unknown's avatar
unknown committed
2290 2291 2292 2293 2294 2295

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

unknown's avatar
unknown committed
2296
  var_free(&v);
2297
  return 0;
2298 2299
}

unknown's avatar
unknown committed
2300

2301 2302 2303 2304 2305 2306 2307 2308 2309 2310
/*
  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.
*/

2311
int my_getc(FILE *file)
2312 2313 2314
{
  if (line_buffer_pos == line_buffer)
    return fgetc(file);
2315
  return *--line_buffer_pos;
2316 2317 2318 2319
}

void my_ungetc(int c)
{
2320
  *line_buffer_pos++= (char) c;
2321 2322 2323 2324
}


my_bool end_of_query(int c)
2325
{
2326
  uint i;
2327
  char tmp[MAX_DELIMITER];
2328

2329 2330 2331 2332
  if (c != *delimiter)
    return 0;

  for (i= 1; i < delimiter_length &&
2333
	 (c= my_getc(cur_file->file)) == *(delimiter + i);
2334
       i++)
2335 2336 2337
    tmp[i]= c;

  if (i == delimiter_length)
2338 2339 2340
    return 1;					/* Found delimiter */

  /* didn't find delimiter, push back things that we read */
2341 2342 2343
  my_ungetc(c);
  while (i > 1)
    my_ungetc(tmp[--i]);
2344 2345 2346 2347
  return 0;
}


2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371
/*
  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
    This function actually reads several lines an adds them to the
    buffer buf. It will continue to read until it finds what it believes
    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
    found inside strings sorrounded with '"' and '\'' escaped strings.

    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.

*/

2372
int read_line(char *buf, int size)
unknown's avatar
unknown committed
2373 2374
{
  int c;
2375
  char quote;
2376
  char *p= buf, *buf_end= buf + size - 1;
2377
  int no_save= 0;
2378 2379
  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
2380
  DBUG_ENTER("read_line");
2381
  LINT_INIT(quote);
2382

2383
  start_lineno= *lineno;
2384 2385
  for (; p < buf_end ;)
  {
2386
    no_save= 0;
2387 2388
    c= my_getc(cur_file->file);
    if (feof(cur_file->file))
unknown's avatar
unknown committed
2389
    {
unknown's avatar
unknown committed
2390
  found_eof:
unknown's avatar
unknown committed
2391 2392
      if (cur_file->file != stdin)
      {
2393
	my_fclose(cur_file->file, MYF(0));
2394 2395
        cur_file->file= 0;
      }
2396 2397
      my_free((gptr)cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR));
      cur_file->file_name= 0;
unknown's avatar
unknown committed
2398
      lineno--;
2399
      start_lineno= *lineno;
2400
      if (cur_file == file_stack)
2401
      {
2402 2403 2404 2405 2406 2407 2408 2409
        /* We're back at the first file, check if
           all { have matching }
         */
        if (cur_block != block_stack)
        {
          start_lineno= *(lineno+1);
          die("Missing end of block");
        }
2410
        DBUG_PRINT("info", ("end of file"));
unknown's avatar
unknown committed
2411
	DBUG_RETURN(1);
2412
      }
2413
      cur_file--;
unknown's avatar
unknown committed
2414
      continue;
2415
    }
2416

2417 2418 2419 2420
    /* Line counting is independent of state */
    if (c == '\n')
      (*lineno)++;

2421 2422
    switch(state) {
    case R_NORMAL:
2423
      /*  Only accept '{' in the beginning of a line */
2424 2425 2426
      if (end_of_query(c))
      {
	*p= 0;
unknown's avatar
unknown committed
2427
	DBUG_RETURN(0);
2428
      }
2429 2430 2431 2432 2433
      else if (c == '\'' || c == '"' || c == '`')
      {
        quote= c;
	state= R_Q;
      }
2434
      else if (c == '\n')
2435
      {
2436
	state = R_LINE_START;
2437
      }
2438 2439 2440 2441
      break;
    case R_COMMENT:
      if (c == '\n')
      {
2442
	*p= 0;
unknown's avatar
unknown committed
2443
	DBUG_RETURN(0);
2444 2445 2446
      }
      break;
    case R_LINE_START:
2447
      /* Only accept start of comment if this is the first line in query */
unknown's avatar
unknown committed
2448
      if ((*lineno == start_lineno) && (c == '#' || c == '-' || parsing_disabled))
2449 2450 2451
      {
	state = R_COMMENT;
      }
2452
      else if (my_isspace(charset_info, c))
2453 2454
      {
	if (c == '\n')
2455
	  start_lineno= *lineno;		/* Query hasn't started yet */
2456
	no_save= 1;
2457
      }
2458 2459
      else if (c == '}')
      {
2460 2461
	*buf++= '}';
	*buf= 0;
unknown's avatar
unknown committed
2462
	DBUG_RETURN(0);
2463
      }
2464
      else if (end_of_query(c) || c == '{')
2465
      {
2466
	*p= 0;
unknown's avatar
unknown committed
2467
	DBUG_RETURN(0);
2468
      }
2469
      else if (c == '\'' || c == '"' || c == '`')
2470
      {
2471 2472
        quote= c;
	state= R_Q;
2473
      }
2474
      else
2475
	state= R_NORMAL;
2476
      break;
unknown's avatar
unknown committed
2477

2478 2479 2480
    case R_Q:
      if (c == quote)
	state= R_Q_IN_Q;
2481
      else if (c == '\\')
2482
	state= R_SLASH_IN_Q;
2483
      break;
2484
    case R_Q_IN_Q:
2485 2486 2487
      if (end_of_query(c))
      {
	*p= 0;
unknown's avatar
unknown committed
2488
	DBUG_RETURN(0);
2489
      }
2490
      if (c != quote)
2491
	state= R_NORMAL;
2492
      else
2493
	state= R_Q;
2494
      break;
2495 2496
    case R_SLASH_IN_Q:
      state= R_Q;
2497
      break;
2498

unknown's avatar
unknown committed
2499
    }
2500 2501

    if (!no_save)
unknown's avatar
unknown committed
2502 2503 2504 2505 2506
    {
      /* 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
2507 2508
      /* We give up if multibyte character is started but not */
      /* completed before we pass buf_end */
unknown's avatar
unknown committed
2509 2510 2511 2512 2513 2514 2515 2516 2517
      if ((charlen > 1) && (p + charlen) <= buf_end)
      {
	int i;
	char* mb_start = p;

	*p++ = c;

	for (i= 1; i < charlen; i++)
	{
2518
	  if (feof(cur_file->file))
unknown's avatar
unknown committed
2519
	    goto found_eof;	/* FIXME: could we just break here?! */
2520
	  c= my_getc(cur_file->file);
unknown's avatar
unknown committed
2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534
	  *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;
    }
2535
  }
2536
  *p= 0;					/* Always end with \0 */
2537
  DBUG_RETURN(feof(cur_file->file));
unknown's avatar
unknown committed
2538 2539
}

2540 2541 2542 2543
/*
  Create a query from a set of lines

  SYNOPSIS
2544
    read_query()
2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558
    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.

    If query starts with @<file_name> this will specify a file to ....
*/
unknown's avatar
unknown committed
2559

2560 2561
static char read_query_buf[MAX_QUERY];

2562
int read_query(struct st_query** q_ptr)
unknown's avatar
unknown committed
2563
{
2564
  char *p= read_query_buf, *p1;
2565
  struct st_query* q;
unknown's avatar
unknown committed
2566
  DBUG_ENTER("read_query");
2567

2568 2569
  if (parser.current_line < parser.read_lines)
  {
2570
    get_dynamic(&q_lines, (gptr) q_ptr, parser.current_line) ;
unknown's avatar
unknown committed
2571
    DBUG_RETURN(0);
2572
  }
2573
  if (!(*q_ptr= q= (struct st_query*) my_malloc(sizeof(*q), MYF(MY_WME))) ||
unknown's avatar
unknown committed
2574 2575
      insert_dynamic(&q_lines, (gptr) &q))
    die(NullS);
2576

2577 2578 2579
  q->record_file[0]= 0;
  q->require_file= 0;
  q->first_word_len= 0;
unknown's avatar
unknown committed
2580

2581
  q->type= Q_UNKNOWN;
2582
  q->query_buf= q->query= 0;
2583
  if (read_line(read_query_buf, sizeof(read_query_buf)))
unknown's avatar
unknown committed
2584
  {
unknown's avatar
unknown committed
2585
    DBUG_RETURN(1);
unknown's avatar
unknown committed
2586
  }
2587
  DBUG_PRINT("info", ("query: %s", read_query_buf));
2588 2589
  if (*p == '#')
  {
2590
    q->type= Q_COMMENT;
unknown's avatar
unknown committed
2591 2592
    /* This goto is to avoid losing the "expected error" info. */
    goto end;
2593
  }
2594 2595 2596 2597 2598 2599 2600 2601
  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
2602
  if (p[0] == '-' && p[1] == '-')
2603
  {
2604 2605
    q->type= Q_COMMENT_WITH_COMMAND;
    p+= 2;					/* To calculate first word */
2606
  }
2607
  else if (!parsing_disabled)
2608
  {
2609
    while (*p && my_isspace(charset_info, *p))
unknown's avatar
unknown committed
2610
      p++ ;
2611
    if (*p == '@')
unknown's avatar
unknown committed
2612 2613 2614
    {
      p++;
      p1 = q->record_file;
2615
      while (!my_isspace(charset_info, *p) &&
unknown's avatar
unknown committed
2616
	     p1 < q->record_file + sizeof(q->record_file) - 1)
unknown's avatar
unknown committed
2617
	*p1++ = *p++;
2618
      *p1 = 0;
unknown's avatar
unknown committed
2619
    }
2620
  }
unknown's avatar
unknown committed
2621 2622

end:
2623
  while (*p && my_isspace(charset_info, *p))
unknown's avatar
unknown committed
2624
    p++;
2625
  if (!(q->query_buf= q->query= my_strdup(p, MYF(MY_WME))))
2626 2627 2628
    die(NullS);

  /* Calculate first word and first argument */
2629 2630 2631
  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
2632
    p++;
2633 2634
  q->first_argument= p;
  q->end= strend(q->query);
2635
  parser.read_lines++;
unknown's avatar
unknown committed
2636
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2637 2638
}

2639 2640 2641

static struct my_option my_long_options[] =
{
2642
  {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
2643 2644 2645
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
  {"database", 'D', "Database to use.", (gptr*) &db, (gptr*) &db, 0,
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2646
  {"basedir", 'b', "Basedir for tests.", (gptr*) &opt_basedir,
2647
   (gptr*) &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2648
  {"big-test", 'B', "Define BIG_TEST to 1.", (gptr*) &opt_big_test,
2649
   (gptr*) &opt_big_test, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
2650
  {"compress", 'C', "Use the compressed server/client protocol.",
2651 2652 2653 2654 2655 2656
   (gptr*) &opt_compress, (gptr*) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
   0, 0, 0},
  {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
   0, 0, 0, 0, 0, 0},
  {"host", 'h', "Connect to host.", (gptr*) &host, (gptr*) &host, 0,
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2657
  {"manager-user", OPT_MANAGER_USER, "Undocumented: Used for debugging.",
2658 2659
   (gptr*) &manager_user, (gptr*) &manager_user, 0, GET_STR, REQUIRED_ARG, 0,
   0, 0, 0, 0, 0},
2660
  {"manager-host", OPT_MANAGER_HOST, "Undocumented: Used for debugging.",
2661 2662
   (gptr*) &manager_host, (gptr*) &manager_host, 0, GET_STR, REQUIRED_ARG,
   0, 0, 0, 0, 0, 0},
2663
  {"manager-password", OPT_MANAGER_PASSWD, "Undocumented: Used for debugging.",
2664
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2665
  {"manager-port", OPT_MANAGER_PORT, "Undocumented: Used for debugging.",
2666 2667 2668
   (gptr*) &manager_port, (gptr*) &manager_port, 0, GET_INT, REQUIRED_ARG,
   MYSQL_MANAGER_PORT, 0, 0, 0, 0, 0},
  {"manager-wait-timeout", OPT_MANAGER_WAIT_TIMEOUT,
2669
   "Undocumented: Used for debugging.", (gptr*) &manager_wait_timeout,
2670 2671 2672 2673
   (gptr*) &manager_wait_timeout, 0, GET_INT, REQUIRED_ARG, 3, 0, 0, 0, 0, 0},
  {"password", 'p', "Password to use when connecting to server.",
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
  {"port", 'P', "Port number to use for connection.", (gptr*) &port,
unknown's avatar
unknown committed
2674
   (gptr*) &port, 0, GET_INT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0, 0},
2675 2676 2677
  {"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},
2678 2679 2680 2681 2682 2683 2684
  {"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},
2685
  {"server-arg", 'A', "Send enbedded server this as a paramenter.",
2686
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2687
  {"server-file", 'F', "Read embedded server arguments from file.",
2688 2689 2690
   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},
2691
  {"skip-safemalloc", OPT_SKIP_SAFEMALLOC,
2692
   "Don't use the memory allocation checking.", 0, 0, 0, GET_NO_ARG, NO_ARG,
2693
   0, 0, 0, 0, 0, 0},
2694
  {"sleep", 'T', "Sleep always this many seconds on sleep commands.",
2695 2696 2697 2698 2699
   (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
2700
#include "sslopt-longopts.h"
2701 2702
  {"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
2703 2704
  {"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},
2705
  {"tmpdir", 't', "Temporary directory where sockets are put.",
2706 2707 2708 2709 2710 2711 2712 2713
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"user", 'u', "User for login.", (gptr*) &user, (gptr*) &user, 0, GET_STR,
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"verbose", 'v', "Write more.", (gptr*) &verbose, (gptr*) &verbose, 0,
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"version", 'V', "Output version information and exit.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
unknown's avatar
unknown committed
2714 2715
};

unknown's avatar
unknown committed
2716 2717 2718

#include <help_start.h>

unknown's avatar
unknown committed
2719 2720 2721 2722 2723 2724 2725 2726 2727
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();
2728
  printf("MySQL AB, by Sasha, Matt, Monty & Jani\n");
unknown's avatar
unknown committed
2729 2730 2731
  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);
2732
  my_print_help(my_long_options);
unknown's avatar
unknown committed
2733
  printf("  --no-defaults       Don't read default options from any options file.\n");
2734
  my_print_variables(my_long_options);
unknown's avatar
unknown committed
2735 2736
}

unknown's avatar
unknown committed
2737 2738
#include <help_end.h>

unknown's avatar
unknown committed
2739 2740
#include <help_end.h>

2741 2742 2743 2744 2745

static my_bool
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
	       char *argument)
{
2746
  switch(optid) {
2747
  case '#':
unknown's avatar
unknown committed
2748
#ifndef DBUG_OFF
2749
    DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysqltest.trace");
unknown's avatar
unknown committed
2750
#endif
2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768
    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);
2769
      DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0);
2770 2771
      if (!(cur_file->file=
            my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
2772 2773
	die("Could not open %s: errno = %d", buff, errno);
      cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
2774 2775
      break;
    }
unknown's avatar
unknown committed
2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788
  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;
    }
2789 2790 2791 2792 2793 2794
  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 */
2795
      tty_password= 0;
2796 2797 2798 2799
    }
    else
      tty_password= 1;
    break;
unknown's avatar
unknown committed
2800
#include <sslopt-case.h>
2801 2802 2803 2804 2805 2806 2807 2808 2809
  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*) "";
    }
2810 2811 2812
    if (embedded_server_arg_count == MAX_SERVER_ARGS-1 ||
        !(embedded_server_args[embedded_server_arg_count++]=
          my_strdup(argument, MYF(MY_FAE))))
2813 2814 2815 2816 2817 2818 2819 2820
    {
      die("Can't use server argument");
    }
    break;
  case 'F':
    if (read_server_arguments(argument))
      die(NullS);
    break;
2821 2822 2823 2824 2825
  case OPT_SKIP_SAFEMALLOC:
#ifdef SAFEMALLOC
    sf_malloc_quick=1;
#endif
    break;
2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836
  case 'V':
    print_version();
    exit(0);
  case '?':
    usage();
    exit(1);
  }
  return 0;
}


unknown's avatar
unknown committed
2837 2838 2839
int parse_args(int argc, char **argv)
{
  load_defaults("my",load_default_groups,&argc,&argv);
2840
  default_argv= argv;
2841

2842
  if ((handle_options(&argc, &argv, my_long_options, get_one_option)))
unknown's avatar
unknown committed
2843
    exit(1);
unknown's avatar
unknown committed
2844 2845 2846 2847 2848 2849 2850

  if (argc > 1)
  {
    usage();
    exit(1);
  }
  if (argc == 1)
2851
    db= *argv;
unknown's avatar
unknown committed
2852 2853 2854 2855 2856 2857
  if (tty_password)
    pass=get_tty_password(NullS);

  return 0;
}

2858
char* safe_str_append(char *buf, const char *str, int size)
unknown's avatar
unknown committed
2859 2860
{
  int i,c ;
2861
  for (i = 0; (c = *str++) &&  i < size - 1; i++)
unknown's avatar
unknown committed
2862 2863 2864 2865 2866
    *buf++ = c;
  *buf = 0;
  return buf;
}

2867
void str_to_file(const char *fname, char *str, int size)
unknown's avatar
unknown committed
2868 2869
{
  int fd;
2870 2871 2872 2873 2874 2875 2876
  char buff[FN_REFLEN];
  if (!test_if_hard_path(fname))
  {
    strxmov(buff, opt_basedir, fname, NullS);
    fname=buff;
  }
  fn_format(buff,fname,"","",4);
unknown's avatar
unknown committed
2877

2878
  if ((fd = my_open(buff, O_WRONLY | O_CREAT | O_TRUNC,
2879
		    MYF(MY_WME | MY_FFNF))) < 0)
2880
    die("Could not open %s: errno = %d", buff, errno);
2881
  if (my_write(fd, (byte*)str, size, MYF(MY_WME|MY_FNABP)))
unknown's avatar
unknown committed
2882 2883 2884 2885
    die("write failed");
  my_close(fd, MYF(0));
}

2886
void reject_dump(const char *record_file, char *buf, int size)
unknown's avatar
unknown committed
2887
{
unknown's avatar
unknown committed
2888
  char reject_file[FN_REFLEN];
unknown's avatar
unknown committed
2889
  str_to_file(fn_format(reject_file, record_file,"",".reject",2), buf, size);
unknown's avatar
unknown committed
2890 2891
}

2892 2893 2894

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

unknown's avatar
unknown committed
2895 2896
static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
				      int len)
2897 2898 2899 2900 2901
{
  if (glob_replace)
  {
    len=(int) replace_strings(glob_replace, &out_buff, &out_length, val);
    if (len == -1)
2902
      die("Out of memory in replace");
2903 2904 2905 2906 2907
    val=out_buff;
  }
  dynstr_append_mem(ds, val, len);
}

2908 2909 2910 2911 2912 2913 2914
/* Append zero-terminated string to ds, with optional replace */

static void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val)
{
  replace_dynstr_append_mem(ds, val, strlen(val));
}

2915

2916 2917
/*
  Append all results to the dynamic string separated with '\t'
2918
  Values may be converted with 'replace_column'
2919 2920 2921 2922 2923
*/

static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
{
  MYSQL_ROW row;
2924
  uint num_fields= mysql_num_fields(res);
2925
  MYSQL_FIELD *fields= !display_result_vertically ? 0 : mysql_fetch_fields(res);
2926 2927 2928
  unsigned long *lengths;
  while ((row = mysql_fetch_row(res)))
  {
2929
    uint i;
2930 2931 2932 2933 2934
    lengths = mysql_fetch_lengths(res);
    for (i = 0; i < num_fields; i++)
    {
      const char *val= row[i];
      ulonglong len= lengths[i];
2935 2936 2937 2938 2939 2940

      if (i < max_replace_column && replace_column[i])
      {
	val= replace_column[i];
	len= strlen(val);
      }
2941 2942
      if (!val)
      {
2943 2944
	val= "NULL";
	len= 4;
2945
      }
2946 2947 2948 2949
      if (!display_result_vertically)
      {
	if (i)
	  dynstr_append_mem(ds, "\t", 1);
2950
	replace_dynstr_append_mem(ds, val, (int)len);
2951 2952 2953 2954
      }
      else
      {
	dynstr_append(ds, fields[i].name);
2955
	dynstr_append_mem(ds, "\t", 1);
2956
	replace_dynstr_append_mem(ds, val, (int)len);
2957 2958
	dynstr_append_mem(ds, "\n", 1);
      }
2959
    }
2960 2961
    if (!display_result_vertically)
      dynstr_append_mem(ds, "\n", 1);
2962
  }
2963
  free_replace_column();
2964 2965
}

2966

2967
/*
2968
* flags control the phased/stages of query execution to be performed
2969 2970 2971
* 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
*/
2972

2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994
static int run_query_normal(MYSQL *mysql, struct st_query *q, int flags);
static int run_query_stmt  (MYSQL *mysql, struct st_query *q, int flags);
static void run_query_stmt_handle_warnings(MYSQL *mysql, DYNAMIC_STRING *ds);
static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields,
                                       DYNAMIC_STRING *ds);

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

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

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

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

  if (ps_protocol_enabled && disable_info &&
      (flags & QUERY_SEND) && (flags & QUERY_REAP) && ps_match_re(q->query))
2995
    return run_query_stmt(mysql, q, flags);
2996 2997 2998 2999 3000
  return run_query_normal(mysql, q, flags);
}


static int run_query_normal(MYSQL* mysql, struct st_query* q, int flags)
unknown's avatar
unknown committed
3001
{
3002
  MYSQL_RES* res= 0;
3003 3004
  uint i;
  int error= 0, err= 0, counter= 0;
3005 3006
  DYNAMIC_STRING *ds;
  DYNAMIC_STRING ds_tmp;
3007
  DYNAMIC_STRING eval_query;
3008
  char* query;
3009
  int query_len, got_error_on_send= 0;
3010
  DBUG_ENTER("run_query_normal");
unknown's avatar
unknown committed
3011
  DBUG_PRINT("enter",("flags: %d", flags));
unknown's avatar
unknown committed
3012
  
unknown's avatar
unknown committed
3013 3014 3015 3016 3017
  if (q->type != Q_EVAL)
  {
    query = q->query;
    query_len = strlen(query);
  }
3018
  else
unknown's avatar
unknown committed
3019 3020 3021 3022 3023 3024
  {
    init_dynamic_string(&eval_query, "", 16384, 65536);
    do_eval(&eval_query, q->query);
    query = eval_query.str;
    query_len = eval_query.length;
  }
unknown's avatar
unknown committed
3025
  DBUG_PRINT("enter", ("query: '%-.60s'", query));
unknown's avatar
unknown committed
3026

unknown's avatar
unknown committed
3027
  if (q->record_file[0])
3028
  {
3029
    init_dynamic_string(&ds_tmp, "", 16384, 65536);
3030 3031
    ds = &ds_tmp;
  }
3032 3033
  else
    ds= &ds_res;
unknown's avatar
unknown committed
3034

3035 3036 3037
  if (flags & QUERY_SEND)
  {
    got_error_on_send= mysql_send_query(mysql, query, query_len);
3038
    if (got_error_on_send && q->expected_errno[0].type == ERR_EMPTY)
3039 3040
      die("unable to send query '%s' (mysql_errno=%d , errno=%d)",
	  query, mysql_errno(mysql), errno);
3041
  }
unknown's avatar
unknown committed
3042

3043
  do
3044
  {
3045
    if ((flags & QUERY_SEND) && !disable_query_log && !counter)
3046
    {
3047
      replace_dynstr_append_mem(ds,query, query_len);
3048 3049
      dynstr_append_mem(ds, delimiter, delimiter_length);
      dynstr_append_mem(ds, "\n", 1);
3050
    }
3051 3052 3053
    if (!(flags & QUERY_REAP))
      DBUG_RETURN(0);

3054 3055 3056 3057
    if (got_error_on_send ||
	(!counter && (*mysql->methods->read_query_result)(mysql)) ||
	 (!(last_result= res= mysql_store_result(mysql)) &&
	  mysql_field_count(mysql)))
unknown's avatar
unknown committed
3058
    {
3059 3060
      if (handle_error(query, q, mysql_errno(mysql), mysql_error(mysql),
                       mysql_sqlstate(mysql), ds))
3061
        error= 1;
3062 3063
      goto end;
    }
3064

3065
    if (handle_no_error(q))
3066
    {
3067
      error= 1;
unknown's avatar
unknown committed
3068
      goto end;
3069
    }
3070

3071
    if (!disable_result_log)
3072
    {
unknown's avatar
unknown committed
3073
      ulong affected_rows;    /* Ok to be undef if 'disable_info' is set */
unknown's avatar
unknown committed
3074
      LINT_INIT(affected_rows);
unknown's avatar
unknown committed
3075

3076
      if (res)
3077
      {
3078
	MYSQL_FIELD *field= mysql_fetch_fields(res);
3079
	uint num_fields= mysql_num_fields(res);
3080

3081
	if (display_metadata)
3082 3083
          run_query_display_metadata(field, num_fields, ds);

3084
	if (!display_result_vertically)
3085
	{
3086 3087 3088 3089
	  for (i = 0; i < num_fields; i++)
	  {
	    if (i)
	      dynstr_append_mem(ds, "\t", 1);
3090
	    replace_dynstr_append(ds, field[i].name);
3091 3092
	  }
	  dynstr_append_mem(ds, "\n", 1);
3093 3094
	}
	append_result(ds, res);
3095
      }
unknown's avatar
unknown committed
3096

unknown's avatar
unknown committed
3097 3098 3099 3100 3101 3102 3103
      /*
        Need to call mysql_affected_rows() before the new
        query to find the warnings
      */
      if (!disable_info)
        affected_rows= (ulong)mysql_affected_rows(mysql);

3104 3105 3106 3107 3108 3109 3110
      /*
        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.
      */
      if (!disable_warnings && !mysql_more_results(mysql) &&
          mysql_warning_count(mysql))
3111
      {
3112 3113 3114 3115 3116 3117 3118
	MYSQL_RES *warn_res=0;
	uint count= mysql_warning_count(mysql);
	if (!mysql_real_query(mysql, "SHOW WARNINGS", 13))
	{
	  warn_res= mysql_store_result(mysql);
	}
	if (!warn_res)
3119
	  verbose_msg("Warning count is %u but didn't get any warnings",
3120 3121 3122 3123 3124 3125 3126
		      count);
	else
	{
	  dynstr_append_mem(ds, "Warnings:\n", 10);
	  append_result(ds, warn_res);
	  mysql_free_result(warn_res);
	}
3127
      }
unknown's avatar
unknown committed
3128
      if (!disable_info)
3129
      {
unknown's avatar
unknown committed
3130
	char buf[40];
unknown's avatar
unknown committed
3131
	sprintf(buf,"affected rows: %lu\n", affected_rows);
unknown's avatar
unknown committed
3132 3133 3134 3135 3136 3137 3138
	dynstr_append(ds, buf);
	if (mysql_info(mysql))
	{
	  dynstr_append(ds, "info: ");
	  dynstr_append(ds, mysql_info(mysql));
	  dynstr_append_mem(ds, "\n", 1);
	}
3139
      }
3140
    }
3141

3142 3143 3144
    if (record)
    {
      if (!q->record_file[0] && !result_file)
3145
	die("Missing result file");
3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159
      if (!result_file)
	str_to_file(q->record_file, ds->str, ds->length);
    }
    else if (q->record_file[0])
    {
      error = check_result(ds, q->record_file, q->require_file);
    }
    if (res)
      mysql_free_result(res);
    last_result= 0;
    counter++;
  } while (!(err= mysql_next_result(mysql)));
  if (err >= 1)
    mysql_error(mysql);
3160

3161
end:
3162
  free_replace();
3163
  last_result=0;
3164 3165
  if (ds == &ds_tmp)
    dynstr_free(&ds_tmp);
3166
  if (q->type == Q_EVAL)
3167
    dynstr_free(&eval_query);
3168 3169 3170 3171 3172 3173

  /*
    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.
  */
3174
  var_set_errno(mysql_errno(mysql));
3175
  DBUG_RETURN(error);
unknown's avatar
unknown committed
3176 3177 3178
}


3179
/*
3180
  Handle errors which occurred after execution
3181 3182

  SYNOPSIS
3183
    handle_error()
3184 3185
      query - query string
      q     - query context
3186 3187 3188
      err_errno - error number
      err_error - error message
      err_sqlstate - sql state
3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199
      ds    - dynamic string which is used for output buffer

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

  RETURN VALUE
    0 - OK
    1 - Some other error was expected.
*/

3200 3201 3202
static int handle_error(const char *query, struct st_query *q,
                        unsigned int err_errno, const char *err_error,
                        const char* err_sqlstate, DYNAMIC_STRING *ds)
3203 3204
{
  uint i;
3205 3206
 
  DBUG_ENTER("handle_error");
3207 3208 3209

  if (q->require_file)
    abort_not_supported_test();
3210
 
3211
  if (q->abort_on_error)
3212
    die("query '%s' failed: %d: %s", query,
3213 3214 3215
        err_errno, err_error);

  for (i= 0 ; (uint) i < q->expected_errors ; i++)
3216
  {
3217 3218 3219 3220
    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)))
3221
    {
3222
      if (q->expected_errors == 1)
3223
      {
3224 3225 3226 3227 3228 3229
        /* 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);
3230
      }
3231 3232 3233 3234 3235 3236 3237
      /* 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");
      /* OK */
      DBUG_RETURN(0);
3238
    }
3239
  }
3240

3241
  DBUG_PRINT("info",("i: %d  expected_errors: %d", i, q->expected_errors));
3242

3243 3244 3245 3246 3247
  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);
3248

3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259
  if (i)
  {
    if (q->expected_errno[0].type == ERR_ERRNO)
      verbose_msg("query '%s' failed with wrong errno %d instead of %d...",
                  q->query, err_errno,
                  q->expected_errno[0].code.errnum);
    else
      verbose_msg("query '%s' failed with wrong sqlstate %s instead of %s...",
                  q->query, err_sqlstate,
                  q->expected_errno[0].code.sqlstate);
    DBUG_RETURN(1);
3260
  }
3261 3262 3263 3264 3265 3266 3267 3268

  /*
    If we do not abort on error, failure to run the query does not fail the
    whole test case.
  */
  verbose_msg("query '%s' failed: %d: %s", q->query, err_errno,
              err_error);
  DBUG_RETURN(0);
3269 3270 3271 3272
}


/*
3273
  Handle absence of errors after execution
3274 3275

  SYNOPSIS
3276
    handle_no_error()
3277 3278 3279 3280 3281 3282 3283
      q - context of query

  RETURN VALUE
    0 - OK
    1 - Some error was expected from this query.
*/

3284
static int handle_no_error(struct st_query *q)
3285
{
3286
  DBUG_ENTER("handle_no_error");
3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307

  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 */
    verbose_msg("query '%s' succeeded - should have failed with errno %d...",
                q->query, q->expected_errno[0].code.errnum);
    DBUG_RETURN(1);
  }
  else if (q->expected_errno[0].type == ERR_SQLSTATE &&
           strcmp(q->expected_errno[0].code.sqlstate,"00000") != 0)
  {
    /* SQLSTATE we wanted was != "00000", i.e. not an expected success */
    verbose_msg("query '%s' succeeded - should have failed with sqlstate %s...",
                q->query, q->expected_errno[0].code.sqlstate);
    DBUG_RETURN(1);
  }

  DBUG_RETURN(0);
}

3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321
/****************************************************************************\
 *  If --ps-protocol run ordinary statements using prepared statemnt C API
\****************************************************************************/

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

static int run_query_stmt(MYSQL *mysql, struct st_query *q, int flags)
{
  int error= 0;             /* Function return code if "goto end;" */
  int err;                  /* Temporary storage of return code from calls */
  int query_len, got_error_on_execute;
3322
  ulonglong num_rows;
3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335
  char *query;
  MYSQL_RES *res= NULL;     /* Note that here 'res' is meta data result set */
  DYNAMIC_STRING *ds;
  DYNAMIC_STRING ds_tmp;
  DYNAMIC_STRING eval_query;
  MYSQL_STMT *stmt;
  DBUG_ENTER("run_query_stmt");

  /*
    We must allocate a new stmt for each query in this program becasue this
    may be a new connection.
  */
  if (!(stmt= mysql_stmt_init(mysql)))
3336
    die("unable init stmt structure");
3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379
  
  if (q->type != Q_EVAL)
  {
    query= q->query;
    query_len= strlen(query);
  }
  else
  {
    init_dynamic_string(&eval_query, "", 16384, 65536);
    do_eval(&eval_query, q->query);
    query= eval_query.str;
    query_len= eval_query.length;
  }
  DBUG_PRINT("query", ("'%-.60s'", query));

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

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

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

  if (err != 0)
  {
    if (q->abort_on_error)
    {
3380 3381
      die("query '%s' failed: %d: %s", query,
          mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
3382 3383 3384 3385 3386 3387
    }
    else
    {
      /*
        Preparing is part of normal execution and some errors may be expected
      */
3388 3389 3390
      error= handle_error(query, q, mysql_stmt_errno(stmt),
                          mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt),
                          ds);
3391 3392 3393 3394 3395 3396
      goto end;
    }
  }

  /* We may have got warnings already, collect them if any */
  /* FIXME we only want this if the statement succeeds I think */ 
3397 3398
  if (!disable_ps_warnings)
    run_query_stmt_handle_warnings(mysql, ds);
3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414

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

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

  if ((got_error_on_execute= mysql_stmt_execute(stmt)) != 0) /* 0 == Success */
  {
    if (q->abort_on_error)
    {
      /* We got an error, unexpected */
3415
      die("unable to execute statement '%s': "
3416
          "%s (mysql_stmt_errno=%d returned=%d)",
3417
          query, mysql_stmt_error(stmt),
3418 3419 3420 3421 3422
          mysql_stmt_errno(stmt), got_error_on_execute);
    }
    else
    {
      /* We got an error, maybe expected */
3423 3424 3425
      error= handle_error(query, q, mysql_stmt_errno(stmt),
                          mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt),
                          ds);
3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438
      goto end;
    }
  }

  /*
    We instruct that we want to update the "max_length" field in
     mysql_stmt_store_result(), this is our only way to know how much
     buffer to allocate for result data
  */
  {
    my_bool one= 1;
    if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH,
                            (void*) &one) != 0)
3439
      die("unable to set stmt attribute "
3440
          "'STMT_ATTR_UPDATE_MAX_LENGTH': %s (returned=%d)",
3441
          query, err);
3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452
  }

  /*
    If we got here the statement succeeded and was expected to do so,
    get data. Note that this can still give errors found during execution!
  */
  if ((err= mysql_stmt_store_result(stmt)) != 0)
  {
    if (q->abort_on_error)
    {
      /* We got an error, unexpected */
3453
      die("unable to execute statement '%s': "
3454
          "%s (mysql_stmt_errno=%d returned=%d)",
3455
          query, mysql_stmt_error(stmt),
3456 3457 3458 3459 3460
          mysql_stmt_errno(stmt), got_error_on_execute);
    }
    else
    {
      /* We got an error, maybe expected */
3461 3462 3463
      error= handle_error(query, q, mysql_stmt_errno(stmt),
                          mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt),
                          ds);
3464 3465 3466 3467 3468
      goto end;
    }
  }

  /* If we got here the statement was both executed and read succeesfully */
3469
  if (handle_no_error(q))
3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501
  {
    error= 1;
    goto end;
  }

  num_rows= mysql_stmt_num_rows(stmt);

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

    /* FIXME check error from the above? */

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

    if (!display_result_vertically)
    {
      /* Display the table heading with the names tab separated */
      uint col_idx;
      for (col_idx= 0; col_idx < num_fields; col_idx++)
      {
        if (col_idx)
          dynstr_append_mem(ds, "\t", 1);
3502
        replace_dynstr_append(ds, field[col_idx].name);
3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517
      }
      dynstr_append_mem(ds, "\n", 1);
    }

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

      /* Allocate array with bind structs, lengths and NULL flags */
      bind= (MYSQL_BIND*)      my_malloc(num_fields * sizeof(MYSQL_BIND),
3518
                                         MYF(MY_WME | MY_FAE | MY_ZEROFILL));
3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542
      length= (unsigned long*) my_malloc(num_fields * sizeof(unsigned long),
                                         MYF(MY_WME | MY_FAE));
      is_null= (my_bool*)      my_malloc(num_fields * sizeof(my_bool),
                                         MYF(MY_WME | MY_FAE));

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

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

      /* Fill in the data into the structures created above */
      if ((err= mysql_stmt_bind_result(stmt, bind)) != 0)
3543
        die("unable to bind result to statement '%s': "
3544
            "%s (mysql_stmt_errno=%d returned=%d)",
3545
            query,
3546 3547 3548 3549 3550 3551
            mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err);

      /* Read result from each row */
      for (row_idx= 0; row_idx < num_rows; row_idx++)
      {
        if ((err= mysql_stmt_fetch(stmt)) != 0)
3552
          die("unable to fetch all rows from statement '%s': "
3553
              "%s (mysql_stmt_errno=%d returned=%d)",
3554
              query,
3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576
              mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err);

        /* Read result from each column */
        for (col_idx= 0; col_idx < num_fields; col_idx++)
        {
          /* FIXME is string terminated? */
          const char *val= (const char *)bind[col_idx].buffer;
          ulonglong len= *bind[col_idx].length;
          if (col_idx < max_replace_column && replace_column[col_idx])
          {
            val= replace_column[col_idx];
            len= strlen(val);
          }
          if (*bind[col_idx].is_null)
          {
            val= "NULL";
            len= 4;
          }
          if (!display_result_vertically)
          {
            if (col_idx)                      /* No tab before first col */
              dynstr_append_mem(ds, "\t", 1);
3577
            replace_dynstr_append_mem(ds, val, (int)len);
3578 3579 3580 3581 3582
          }
          else
          {
            dynstr_append(ds, field[col_idx].name);
            dynstr_append_mem(ds, "\t", 1);
3583
            replace_dynstr_append_mem(ds, val, (int)len);
3584 3585 3586 3587 3588 3589 3590 3591
            dynstr_append_mem(ds, "\n", 1);
          }
        }
        if (!display_result_vertically)
          dynstr_append_mem(ds, "\n", 1);
      }

      if ((err= mysql_stmt_fetch(stmt)) != MYSQL_NO_DATA)
3592
        die("fetch didn't end with MYSQL_NO_DATA from statement "
3593
            "'%s': %s (mysql_stmt_errno=%d returned=%d)",
3594
            query,
3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630
            mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err);

      free_replace_column();

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

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

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

  if (record)
  {
    if (!q->record_file[0] && !result_file)
3631
      die("Missing result file");
3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652
    if (!result_file)
      str_to_file(q->record_file, ds->str, ds->length);
  }
  else if (q->record_file[0])
  {
    error= check_result(ds, q->record_file, q->require_file);
  }
  if (res)
    mysql_free_result(res);     /* Free normal result set with meta data */
  last_result= 0;               /* FIXME have no idea what this is about... */

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

end:
  free_replace();
  last_result=0;
  if (ds == &ds_tmp)
    dynstr_free(&ds_tmp);
  if (q->type == Q_EVAL)
    dynstr_free(&eval_query);
3653
  var_set_errno(mysql_stmt_errno(stmt));
3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667
  mysql_stmt_close(stmt);
  DBUG_RETURN(error);
}


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

static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields,
                                       DYNAMIC_STRING *ds)
{
  MYSQL_FIELD *field_end;
  dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t"
unknown's avatar
unknown committed
3668
                "Column_alias\tType\tLength\tMax length\tIs_null\t"
3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694
                "Flags\tDecimals\tCharsetnr\n");

  for (field_end= field+num_fields ;
       field < field_end ;
       field++)
  {
    char buff[22];
    dynstr_append_mem(ds, field->catalog,
                          field->catalog_length);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, field->db, field->db_length);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, field->org_table,
                          field->org_table_length);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, field->table,
                          field->table_length);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, field->org_name,
                          field->org_name_length);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, field->name, field->name_length);
    dynstr_append_mem(ds, "\t", 1);
    int10_to_str((int) field->type, buff, 10);
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\t", 1);
3695
    longlong10_to_str((unsigned int) field->length, buff, 10);
3696 3697
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\t", 1);
3698
    longlong10_to_str((unsigned int) field->max_length, buff, 10);
3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ?
                                   "N" : "Y"), 1);
    dynstr_append_mem(ds, "\t", 1);

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


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

  if (!disable_warnings && (count= mysql_warning_count(mysql)))
  {
3725 3726 3727 3728 3729 3730 3731
    /*
      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));

3732 3733 3734 3735
    if (mysql_real_query(mysql, "SHOW WARNINGS", 13) == 0)
    {
      MYSQL_RES *warn_res= mysql_store_result(mysql);
      if (!warn_res)
3736
        verbose_msg("Warning count is %u but didn't get any warnings",
3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816
                    count);
      else
      {
        dynstr_append_mem(ds, "Warnings:\n", 10);
        append_result(ds, warn_res);
        mysql_free_result(warn_res);
      }
    }
  }
  DBUG_VOID_RETURN;
}


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

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

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


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

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

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


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

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

3817
void get_query_type(struct st_query* q)
3818
{
3819 3820
  char save;
  uint type;
unknown's avatar
unknown committed
3821 3822
  DBUG_ENTER("get_query_type");

3823
  if (!parsing_disabled && *q->query == '}')
3824 3825
  {
    q->type = Q_END_BLOCK;
unknown's avatar
unknown committed
3826
    DBUG_VOID_RETURN;
3827 3828
  }
  if (q->type != Q_COMMENT_WITH_COMMAND)
3829
    q->type= parsing_disabled ? Q_COMMENT : Q_QUERY;
3830

3831 3832
  save=q->query[q->first_word_len];
  q->query[q->first_word_len]=0;
3833
  type=find_type(q->query, &command_typelib, 1+2);
3834
  q->query[q->first_word_len]=save;
3835
  if (type > 0)
3836
  {
unknown's avatar
unknown committed
3837
    q->type=(enum enum_commands) type;		/* Found command */
3838 3839 3840 3841 3842 3843 3844 3845
    /*
      If queries are disabled, only recognize
      --enable-queries and --disable-queries
    */
    if (parsing_disabled && q->type != Q_ENABLE_PARSING &&
        q->type != Q_DISABLE_PARSING)
      q->type= Q_COMMENT;
  }
3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861
  else if (q->type == Q_COMMENT_WITH_COMMAND &&
           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
3862
  DBUG_VOID_RETURN;
3863
}
unknown's avatar
unknown committed
3864

unknown's avatar
unknown committed
3865

unknown's avatar
unknown committed
3866
static byte *get_var_key(const byte* var, uint* len,
unknown's avatar
unknown committed
3867
			 my_bool __attribute__((unused)) t)
unknown's avatar
unknown committed
3868 3869 3870 3871 3872 3873 3874
{
  register char* key;
  key = ((VAR*)var)->name;
  *len = ((VAR*)var)->name_len;
  return (byte*)key;
}

unknown's avatar
unknown committed
3875
static VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
unknown's avatar
unknown committed
3876 3877 3878
		     int val_len)
{
  int val_alloc_len;
unknown's avatar
unknown committed
3879
  VAR *tmp_var;
3880
  if (!name_len && name)
unknown's avatar
unknown committed
3881
    name_len = strlen(name);
3882
  if (!val_len && val)
unknown's avatar
unknown committed
3883 3884
    val_len = strlen(val) ;
  val_alloc_len = val_len + 16; /* room to grow */
3885
  if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var)
3886
						 + name_len, MYF(MY_WME))))
unknown's avatar
unknown committed
3887
    die("Out of memory");
unknown's avatar
unknown committed
3888

3889
  tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0;
3890
  tmp_var->alloced = (v == 0);
3891

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

unknown's avatar
unknown committed
3895
  memcpy(tmp_var->name, name, name_len);
3896 3897 3898 3899 3900
  if (val)
  {
    memcpy(tmp_var->str_val, val, val_len);
    tmp_var->str_val[val_len]=0;
  }
unknown's avatar
unknown committed
3901 3902 3903
  tmp_var->name_len = name_len;
  tmp_var->str_val_len = val_len;
  tmp_var->alloced_len = val_alloc_len;
3904
  tmp_var->int_val = (val) ? atoi(val) : 0;
unknown's avatar
unknown committed
3905 3906 3907 3908
  tmp_var->int_dirty = 0;
  return tmp_var;
}

unknown's avatar
unknown committed
3909
static void var_free(void *v)
unknown's avatar
unknown committed
3910
{
unknown's avatar
unknown committed
3911
  my_free(((VAR*) v)->str_val, MYF(MY_WME));
unknown's avatar
unknown committed
3912 3913
  if (((VAR*)v)->alloced)
   my_free((char*) v, MYF(MY_WME));
unknown's avatar
unknown committed
3914 3915 3916
}


3917
static VAR* var_from_env(const char *name, const char *def_val)
unknown's avatar
unknown committed
3918
{
unknown's avatar
unknown committed
3919 3920
  const char *tmp;
  VAR *v;
3921
  if (!(tmp = getenv(name)))
unknown's avatar
unknown committed
3922
    tmp = def_val;
unknown's avatar
unknown committed
3923

3924
  v = var_init(0, name, strlen(name), tmp, strlen(tmp));
unknown's avatar
SCRUM  
unknown committed
3925
  my_hash_insert(&var_hash, (byte*)v);
3926
  return v;
3927
}
unknown's avatar
unknown committed
3928

3929

unknown's avatar
unknown committed
3930
static void init_var_hash(MYSQL *mysql)
unknown's avatar
unknown committed
3931
{
unknown's avatar
unknown committed
3932
  VAR *v;
3933
  DBUG_ENTER("init_var_hash");
3934
  if (hash_init(&var_hash, charset_info, 
unknown's avatar
unknown committed
3935
                1024, 0, 0, get_var_key, var_free, MYF(0)))
unknown's avatar
unknown committed
3936
    die("Variable hash initialization failed");
3937 3938
  my_hash_insert(&var_hash, (byte*) var_init(0,"BIG_TEST", 0,
                                             (opt_big_test) ? "1" : "0", 0));
unknown's avatar
unknown committed
3939
  v= var_init(0,"MAX_TABLES", 0, (sizeof(ulong) == 4) ? "31" : "62",0);
unknown's avatar
SCRUM  
unknown committed
3940
  my_hash_insert(&var_hash, (byte*) v);
unknown's avatar
unknown committed
3941
  v= var_init(0,"SERVER_VERSION", 0, mysql_get_server_info(mysql), 0);
unknown's avatar
SCRUM  
unknown committed
3942
  my_hash_insert(&var_hash, (byte*) v);
unknown's avatar
unknown committed
3943 3944
  v= var_init(0,"DB", 2, db, 0);
  my_hash_insert(&var_hash, (byte*) v);
3945
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
3946
}
3947

3948

unknown's avatar
unknown committed
3949
int main(int argc, char **argv)
unknown's avatar
unknown committed
3950 3951
{
  int error = 0;
unknown's avatar
unknown committed
3952
  struct st_query *q;
3953 3954
  my_bool require_file=0, q_send_flag=0, abort_flag= 0,
          query_executed= 0;
3955
  char save_file[FN_REFLEN];
3956
  MY_STAT res_info;
3957 3958
  MY_INIT(argv[0]);

unknown's avatar
unknown committed
3959 3960 3961
  /* Use all time until exit if no explicit 'start_timer' */
  timer_start= timer_now();

3962
  save_file[0]=0;
3963
  TMPDIR[0]=0;
unknown's avatar
unknown committed
3964 3965 3966 3967
  memset(cons, 0, sizeof(cons));
  cons_end = cons + MAX_CONS;
  next_con = cons + 1;
  cur_con = cons;
unknown's avatar
unknown committed
3968

unknown's avatar
unknown committed
3969
  memset(file_stack, 0, sizeof(file_stack));
unknown's avatar
unknown committed
3970
  memset(&master_pos, 0, sizeof(master_pos));
3971 3972
  file_stack_end= file_stack + MAX_INCLUDE_DEPTH - 1;
  cur_file= file_stack;
3973
  lineno   = lineno_stack;
3974
  my_init_dynamic_array(&q_lines, sizeof(struct st_query*), INIT_Q_LINES,
3975
		     INIT_Q_LINES);
unknown's avatar
unknown committed
3976

3977
  memset(block_stack, 0, sizeof(block_stack));
3978
  block_stack_end= block_stack + BLOCK_STACK_DEPTH - 1;
unknown's avatar
unknown committed
3979 3980 3981 3982
  cur_block= block_stack;
  cur_block->ok= TRUE; /* Outer block should always be executed */
  cur_block->cmd= cmd_none;

3983
  init_dynamic_string(&ds_res, "", 0, 65536);
unknown's avatar
unknown committed
3984
  parse_args(argc, argv);
3985 3986

  DBUG_PRINT("info",("result_file: '%s'", result_file ? result_file : ""));
3987 3988 3989
  if (mysql_server_init(embedded_server_arg_count,
			embedded_server_args,
			(char**) embedded_server_groups))
unknown's avatar
unknown committed
3990
    die("Can't initialize MySQL server");
3991
  if (cur_file == file_stack && cur_file->file == 0)
3992
  {
3993 3994
    cur_file->file= stdin;
    cur_file->file_name= my_strdup("<stdin>", MYF(MY_WME));
3995
  }
3996
  *lineno=1;
3997 3998 3999
#ifndef EMBEDDED_LIBRARY
  if (manager_host)
    init_manager();
4000
#endif
4001 4002 4003 4004 4005
  if (ps_protocol)
  {
    ps_protocol_enabled= 1;
    ps_init_re();
  }
4006
  if (!( mysql_init(&cur_con->mysql)))
unknown's avatar
unknown committed
4007
    die("Failed in mysql_init()");
unknown's avatar
unknown committed
4008 4009
  if (opt_compress)
    mysql_options(&cur_con->mysql,MYSQL_OPT_COMPRESS,NullS);
4010
  mysql_options(&cur_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
unknown's avatar
unknown committed
4011
  mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name);
unknown's avatar
unknown committed
4012

unknown's avatar
unknown committed
4013 4014 4015 4016 4017
#ifdef HAVE_OPENSSL
  if (opt_use_ssl)
    mysql_ssl_set(&cur_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
		  opt_ssl_capath, opt_ssl_cipher);
#endif
4018

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

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

unknown's avatar
unknown committed
4025 4026
  init_var_hash(&cur_con->mysql);

4027 4028 4029 4030 4031
  /*
    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
  */
4032 4033
  var_set_errno(-1);

unknown's avatar
unknown committed
4034
  while (!abort_flag && !read_query(&q))
4035 4036 4037 4038
  {
    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
4039
    if (cur_block->ok)
unknown's avatar
unknown committed
4040
    {
4041
      q->last_argument= q->first_argument;
4042 4043
      processed = 1;
      switch (q->type) {
4044 4045 4046
      case Q_CONNECT:
        error|= do_connect(q);
        break;
4047
      case Q_CONNECTION: select_connection(q); break;
unknown's avatar
unknown committed
4048
      case Q_DISCONNECT:
4049
      case Q_DIRTY_CLOSE:
unknown's avatar
unknown committed
4050
	close_connection(q); break;
4051
      case Q_RPL_PROBE: do_rpl_probe(q); break;
4052
      case Q_ENABLE_RPL_PARSE:	 do_enable_rpl_parse(q); break;
unknown's avatar
unknown committed
4053
      case Q_DISABLE_RPL_PARSE:  do_disable_rpl_parse(q); break;
4054
      case Q_ENABLE_QUERY_LOG:   disable_query_log=0; break;
unknown's avatar
unknown committed
4055
      case Q_DISABLE_QUERY_LOG:  disable_query_log=1; break;
4056 4057
      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
4058 4059
      case Q_ENABLE_RESULT_LOG:  disable_result_log=0; break;
      case Q_DISABLE_RESULT_LOG: disable_result_log=1; break;
4060 4061
      case Q_ENABLE_WARNINGS:    disable_warnings=0; break;
      case Q_DISABLE_WARNINGS:   disable_warnings=1; break;
4062 4063
      case Q_ENABLE_PS_WARNINGS:    disable_ps_warnings=0; break;
      case Q_DISABLE_PS_WARNINGS:   disable_ps_warnings=1; break;
4064 4065
      case Q_ENABLE_INFO:        disable_info=0; break;
      case Q_DISABLE_INFO:       disable_info=1; break;
4066
      case Q_ENABLE_METADATA:    display_metadata=1; break;
4067
      case Q_DISABLE_METADATA:   display_metadata=0; break;
4068
      case Q_SOURCE: do_source(q); break;
4069 4070
      case Q_SLEEP: do_sleep(q, 0); break;
      case Q_REAL_SLEEP: do_sleep(q, 1); break;
4071
      case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(q); break;
4072
      case Q_REQUIRE_MANAGER: do_require_manager(q); break;
4073
#ifndef EMBEDDED_LIBRARY
4074 4075
      case Q_SERVER_START: do_server_start(q); break;
      case Q_SERVER_STOP: do_server_stop(q); break;
4076
#endif
4077 4078
      case Q_INC: do_modify_var(q, "inc", DO_INC); break;
      case Q_DEC: do_modify_var(q, "dec", DO_DEC); break;
4079 4080
      case Q_ECHO: do_echo(q); break;
      case Q_SYSTEM: do_system(q); break;
4081 4082 4083
      case Q_DELIMITER:
	strmake(delimiter, q->first_argument, sizeof(delimiter) - 1);
	delimiter_length= strlen(delimiter);
4084
        q->last_argument= q->first_argument+delimiter_length;
4085
	break;
4086 4087 4088 4089 4090 4091
      case Q_DISPLAY_VERTICAL_RESULTS:
        display_result_vertically= TRUE;
        break;
      case Q_DISPLAY_HORIZONTAL_RESULTS:
	display_result_vertically= FALSE;
        break;
4092
      case Q_LET: do_let(q); break;
4093
      case Q_EVAL_RESULT:
4094
        eval_result = 1; break;
4095
      case Q_EVAL:
4096
	if (q->query == q->query_buf)
unknown's avatar
unknown committed
4097
        {
unknown's avatar
unknown committed
4098
	  q->query= q->first_argument;
unknown's avatar
unknown committed
4099 4100
          q->first_word_len= 0;
        }
4101
	/* fall through */
4102
      case Q_QUERY_VERTICAL:
4103
      case Q_QUERY_HORIZONTAL:
4104 4105 4106 4107 4108 4109
      {
	my_bool old_display_result_vertically= display_result_vertically;
	if (!q->query[q->first_word_len])
	{
	  /* This happens when we use 'query_..' on it's own line */
	  q_send_flag=1;
unknown's avatar
unknown committed
4110 4111 4112
          DBUG_PRINT("info",
                     ("query: '%s' first_word_len: %d  send_flag=1",
                      q->query, q->first_word_len));
4113 4114 4115 4116 4117
	  break;
	}
	/* fix up query pointer if this is * first iteration for this line */
	if (q->query == q->query_buf)
	  q->query += q->first_word_len + 1;
4118
	display_result_vertically= (q->type==Q_QUERY_VERTICAL);
4119 4120 4121 4122 4123 4124
	if (save_file[0])
	{
	  strmov(q->record_file,save_file);
	  q->require_file=require_file;
	  save_file[0]=0;
	}
unknown's avatar
unknown committed
4125
	error|= run_query(&cur_con->mysql, q, QUERY_REAP|QUERY_SEND);
4126
	display_result_vertically= old_display_result_vertically;
4127
        q->last_argument= q->end;
4128
        query_executed= 1;
4129 4130
	break;
      }
4131
      case Q_QUERY:
4132
      case Q_REAP:
4133
      {
4134 4135 4136 4137 4138
	/*
	  We read the result always regardless of the mode for both full
	  query and read-result only (reap)
	*/
	int flags = QUERY_REAP;
4139
	if (q->type != Q_REAP) /* for a full query, enable the send stage */
unknown's avatar
unknown committed
4140
	  flags |= QUERY_SEND;
unknown's avatar
unknown committed
4141 4142 4143 4144 4145
	if (q_send_flag)
	{
	  flags= QUERY_SEND;
	  q_send_flag=0;
	}
4146
	if (save_file[0])
4147
	{
4148 4149 4150
	  strmov(q->record_file,save_file);
	  q->require_file=require_file;
	  save_file[0]=0;
4151
	}
unknown's avatar
unknown committed
4152
	error |= run_query(&cur_con->mysql, q, flags);
4153
	query_executed= 1;
4154
        q->last_argument= q->end;
unknown's avatar
unknown committed
4155
	break;
4156
      }
unknown's avatar
unknown committed
4157
      case Q_SEND:
unknown's avatar
unknown committed
4158 4159 4160 4161 4162 4163 4164 4165
	if (!q->query[q->first_word_len])
	{
	  /* This happens when we use 'send' on it's own line */
	  q_send_flag=1;
	  break;
	}
	/* fix up query pointer if this is * first iteration for this line */
	if (q->query == q->query_buf)
4166
	  q->query += q->first_word_len;
4167 4168 4169 4170 4171
	/*
	  run_query() can execute a query partially, depending on the flags
	  QUERY_SEND flag without QUERY_REAP tells it to just send the
	  query and read the result some time later when reap instruction
	  is given on this connection.
4172
	 */
4173
	error |= run_query(&cur_con->mysql, q, QUERY_SEND);
4174
	query_executed= 1;
4175
        q->last_argument= q->end;
unknown's avatar
unknown committed
4176
	break;
4177 4178 4179 4180
      case Q_RESULT:
	get_file_name(save_file,q);
	require_file=0;
	break;
unknown's avatar
unknown committed
4181
      case Q_ERROR:
4182
        global_expected_errors=get_errcodes(global_expected_errno,q);
unknown's avatar
unknown committed
4183
	break;
4184 4185 4186 4187
      case Q_REQUIRE:
	get_file_name(save_file,q);
	require_file=1;
	break;
unknown's avatar
unknown committed
4188 4189 4190
      case Q_REPLACE:
	get_replace(q);
	break;
4191 4192 4193
      case Q_REPLACE_COLUMN:
	get_replace_column(q);
	break;
4194 4195
      case Q_SAVE_MASTER_POS: do_save_master_pos(); break;
      case Q_SYNC_WITH_MASTER: do_sync_with_master(q); break;
4196 4197 4198 4199
      case Q_SYNC_SLAVE_WITH_MASTER:
      {
	do_save_master_pos();
	if (*q->first_argument)
4200
	  select_connection(q);
4201
	else
4202 4203
	  select_connection_name("slave");
	do_sync_with_master2(0);
4204 4205
	break;
      }
4206
      case Q_COMMENT:				/* Ignore row */
4207
      case Q_COMMENT_WITH_COMMAND:
4208
        q->last_argument= q->end;
4209
	break;
4210 4211 4212
      case Q_PING:
	(void) mysql_ping(&cur_con->mysql);
	break;
4213
      case Q_EXEC:
unknown's avatar
unknown committed
4214
	do_exec(q);
4215
	query_executed= 1;
4216
	break;
unknown's avatar
unknown committed
4217 4218 4219 4220 4221 4222 4223 4224 4225
      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;
4226
      case Q_CHARACTER_SET:
unknown's avatar
unknown committed
4227 4228
	set_charset(q);
	break;
4229 4230 4231 4232 4233 4234
      case Q_DISABLE_PS_PROTOCOL:
        ps_protocol_enabled= 0;
        break;
      case Q_ENABLE_PS_PROTOCOL:
        ps_protocol_enabled= ps_protocol;
        break;
4235
      case Q_DISABLE_RECONNECT:
4236 4237 4238
      {
        my_bool reconnect= 0;
        mysql_options(&cur_con->mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
4239
        break;
4240
      }
4241
      case Q_ENABLE_RECONNECT:
4242 4243 4244
      {
        my_bool reconnect= 1;
        mysql_options(&cur_con->mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
4245
        break;
4246
      }
4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257
      case Q_DISABLE_PARSING:
        parsing_disabled++;
        break;
      case Q_ENABLE_PARSING:
        /*
          Ensure we don't get parsing_disabled < 0 as this would accidently
          disable code we don't want to have disabled
        */
        if (parsing_disabled > 0)
          parsing_disabled--;
        break;
4258

unknown's avatar
unknown committed
4259 4260 4261
      case Q_EXIT:
        abort_flag= 1;
        break;
4262 4263 4264 4265

      default:
        processed= 0;
        break;
4266 4267
      }
    }
4268

4269 4270
    if (!processed)
    {
4271
      current_line_inc= 0;
unknown's avatar
unknown committed
4272
      switch (q->type) {
unknown's avatar
unknown committed
4273 4274
      case Q_WHILE: do_block(cmd_while, q); break;
      case Q_IF: do_block(cmd_if, q); break;
4275 4276 4277
      case Q_END_BLOCK: do_done(q); break;
      default: current_line_inc = 1; break;
      }
unknown's avatar
unknown committed
4278
    }
4279 4280
    else
      check_eol_junk(q->last_argument);
unknown's avatar
unknown committed
4281

4282 4283 4284 4285 4286 4287 4288 4289 4290
    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
4291

4292 4293 4294
    parser.current_line += current_line_inc;
  }

4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306
  if (!query_executed && result_file && my_stat(result_file, &res_info, 0))
  {
    /*
      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.
      Note that we don't care, if my_stat() fails. For example for
      non-existing or non-readable file we assume it's fine to have
      no query output from the test file, e.g. regarded as no error.
    */
    if (res_info.st_size)
      error|= (RESULT_CONTENT_MISMATCH | RESULT_LENGTH_MISMATCH);
  }
4307
  if (result_file && ds_res.length && !error)
4308
  {
4309
    if (!record)
4310 4311
      error |= check_result(&ds_res, result_file, q->require_file);
    else
4312
      str_to_file(result_file, ds_res.str, ds_res.length);
4313
  }
4314
  dynstr_free(&ds_res);
4315

unknown's avatar
unknown committed
4316 4317
  if (!silent)
  {
4318
    if (error)
unknown's avatar
unknown committed
4319 4320 4321 4322
      printf("not ok\n");
    else
      printf("ok\n");
  }
4323

unknown's avatar
unknown committed
4324 4325
  if (!got_end_timer)
    timer_output();				/* No end_timer cmd, end it */
4326
  free_used_memory();
4327
  my_end(MY_CHECK_ERROR);
4328 4329
  exit(error ? 1 : 0);
  return error ? 1 : 0;				/* Keep compiler happy */
unknown's avatar
unknown committed
4330
}
unknown's avatar
unknown committed
4331

4332

4333 4334 4335 4336 4337 4338
/*
  Read arguments for embedded server and put them into
  embedded_server_args_count and embedded_server_args[]
*/


unknown's avatar
unknown committed
4339
static int read_server_arguments(const char *name)
4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355
{
  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
4356
  if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377
    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
4378

unknown's avatar
unknown committed
4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405
/****************************************************************************\
 *
 *  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)
  {
4406
    char buf[32], *end;
unknown's avatar
unknown committed
4407
    ulonglong timer= timer_now() - timer_start;
4408 4409
    end= longlong2str(timer, buf, 10);
    str_to_file(timer_file,buf, (int) (end-buf));
unknown's avatar
unknown committed
4410 4411 4412 4413 4414 4415 4416 4417
  }
}

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

unknown's avatar
unknown committed
4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430
/****************************************************************************
* 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 {
4431
  bool	 found;
unknown's avatar
unknown committed
4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536
  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 {
4537 4538
  uint	*bits;				/* Pointer to used sets */
  short next[LAST_CHAR_CODE];		/* Pointer to next sets */
unknown's avatar
unknown committed
4539 4540
  uint	found_len;			/* Best match to date */
  int	found_offset;
4541 4542
  uint	table_offset;
  uint	size_of_bits;			/* For convinience */
unknown's avatar
unknown committed
4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570
} 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);
4571 4572
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
4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648
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] == '^')
    {
4649
      internal_set_bit(start_states,states+1);
unknown's avatar
unknown committed
4650 4651 4652 4653 4654 4655 4656 4657
      if (!from[i][2])
      {
	start_states->table_offset=i;
	start_states->found_offset=1;
      }
    }
    else if (from[i][0] == '\\' && from[i][1] == '$')
    {
4658 4659
      internal_set_bit(start_states,states);
      internal_set_bit(word_states,states);
unknown's avatar
unknown committed
4660 4661 4662 4663 4664 4665 4666 4667
      if (!from[i][2] && start_states->table_offset == (uint) ~0)
      {
	start_states->table_offset=i;
	start_states->found_offset=0;
      }
    }
    else
    {
4668
      internal_set_bit(word_states,states);
unknown's avatar
unknown committed
4669
      if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2]))
4670
	internal_set_bit(start_states,states+1);
unknown's avatar
unknown committed
4671
      else
4672
	internal_set_bit(start_states,states);
unknown's avatar
unknown committed
4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777
    }
    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)
4778
	      internal_set_bit(new_set,i+1);		/* To next set */
unknown's avatar
unknown committed
4779
	    else
4780
	      internal_set_bit(new_set,i);
unknown's avatar
unknown committed
4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796
	  }
	}
	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)))
4797
	      internal_clear_bit(new_set,i);
unknown's avatar
unknown committed
4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945
	    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;
}

4946
static void internal_set_bit(REP_SET *set, uint bit)
unknown's avatar
unknown committed
4947 4948 4949 4950 4951
{
  set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
  return;
}

4952
static void internal_clear_bit(REP_SET *set, uint bit)
unknown's avatar
unknown committed
4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 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 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072
{
  set->bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT));
  return;
}


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

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

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


	/* Get next set bit from set. */

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

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

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

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

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

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

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

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

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

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


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


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

5073
uint replace_strings(REPLACE *rep, my_string *start,uint *max_length,
unknown's avatar
unknown committed
5074
		     const char *from)
unknown's avatar
unknown committed
5075 5076 5077
{
  reg1 REPLACE *rep_pos;
  reg2 REPLACE_STRING *rep_str;
unknown's avatar
unknown committed
5078
  my_string to,end,pos,new_str;
unknown's avatar
unknown committed
5079 5080 5081

  end=(to= *start) + *max_length-1;
  rep_pos=rep+1;
5082
  for (;;)
unknown's avatar
unknown committed
5083 5084 5085 5086 5087 5088 5089
  {
    while (!rep_pos->found)
    {
      rep_pos= rep_pos->next[(uchar) *from];
      if (to == end)
      {
	(*max_length)+=8192;
unknown's avatar
unknown committed
5090
	if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME))))
unknown's avatar
unknown committed
5091
	  return (uint) -1;
unknown's avatar
unknown committed
5092 5093
	to=new_str+(to - *start);
	end=(*start=new_str)+ *max_length-1;
unknown's avatar
unknown committed
5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104
      }
      *to++= *from++;
    }
    if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string)
      return (uint) (to - *start)-1;
    to-=rep_str->to_offset;
    for (pos=rep_str->replace_string; *pos ; pos++)
    {
      if (to == end)
      {
	(*max_length)*=2;
unknown's avatar
unknown committed
5105
	if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME))))
unknown's avatar
unknown committed
5106
	  return (uint) -1;
unknown's avatar
unknown committed
5107 5108
	to=new_str+(to - *start);
	end=(*start=new_str)+ *max_length-1;
unknown's avatar
unknown committed
5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129
      }
      *to++= *pos;
    }
    if (!*(from-=rep_str->from_offset) && rep_pos->found != 2)
      return (uint) (to - *start);
    rep_pos=rep;
  }
}

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

static void free_replace_buffer(void)
{
  my_free(out_buff,MYF(MY_WME));
}
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


/****************************************************************************
 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)
5166
    die("Missing argument in %s", q->query);
5167 5168 5169 5170 5171 5172 5173 5174 5175 5176

  /* 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)
5177
      die("Wrong column number to replace_column in '%s'", q->query);
5178
    if (!*from)
5179
      die("Wrong number of arguments to replace_column in '%s'", q->query);
5180 5181 5182 5183 5184 5185
    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));
5186
  q->last_argument= q->end;
5187
}
unknown's avatar
unknown committed
5188

unknown's avatar
unknown committed
5189
#if defined(__NETWARE__) || defined(__WIN__)
unknown's avatar
unknown committed
5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210
/*
  Substitute environment variables with text.

  SYNOPSIS
    subst_env_var()
    arg			String that should be substitute

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

  NOTES
    Return string must be freed with my_free()

  RETURN
    String with environment variables replaced.
*/

static char *subst_env_var(const char *str)
{
  char *result;
5211
  char *pos;
unknown's avatar
unknown committed
5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230

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

      /* Search for end of environment variable */
      for (str++;
           *str && !isspace(*str) && *str != '\\' && *str != '/' &&
             *str != '$';
           str++)
5231
        *env_pos++= *str;
unknown's avatar
unknown committed
5232 5233 5234 5235 5236
      *env_pos= 0;

      if (!(subst= getenv(env_var)))
      {
        my_free(result, MYF(0));
5237
        die("MYSQLTEST.NLM: Environment variable %s is not defined",
unknown's avatar
unknown committed
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
            env_var);
      }

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


/*
  popen replacement for Netware

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

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

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

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

#undef popen                                    /* Remove wrapper */
unknown's avatar
unknown committed
5273 5274 5275
#ifdef __WIN__ 
#define popen _popen                           /* redefine for windows */
#endif
unknown's avatar
unknown committed
5276

5277
FILE *my_popen(const char *cmd, const char *mode __attribute__((unused)))
unknown's avatar
unknown committed
5278 5279
{
  char *subst_cmd;
5280 5281
  FILE *res_file;

unknown's avatar
unknown committed
5282 5283 5284 5285 5286 5287
  subst_cmd= subst_env_var(cmd);
  res_file= popen(subst_cmd, "r0");
  my_free(subst_cmd, MYF(0));
  return res_file;
}

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