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

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

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

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

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

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

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

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

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

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

45
#define MTEST_VERSION "2.6"
46

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

unknown's avatar
unknown committed
91 92 93 94 95 96 97
/*
  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
98
#define MAX_CON_TRIES	5
unknown's avatar
unknown committed
99

100
#define SLAVE_POLL_INTERVAL 300000 /* 0.3 of a sec */
101 102
#define DEFAULT_DELIMITER ";"
#define MAX_DELIMITER 16
103

104 105 106
#define RESULT_OK 0
#define RESULT_CONTENT_MISMATCH 1
#define RESULT_LENGTH_MISMATCH 2
107

108
enum {OPT_MANAGER_USER=256,OPT_MANAGER_HOST,OPT_MANAGER_PASSWD,
unknown's avatar
unknown committed
109 110
      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,
111 112
      OPT_SSL_CIPHER,OPT_PS_PROTOCOL,OPT_SP_PROTOCOL,OPT_CURSOR_PROTOCOL,
      OPT_VIEW_PROTOCOL};
unknown's avatar
unknown committed
113

114 115
/* ************************************************************************ */
/*
116 117 118 119 120
  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.
121
*/
unknown's avatar
unknown committed
122

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
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;

140 141 142 143 144 145
typedef struct
{
  const char *name;
  long        code;
} st_error;

146 147
static st_error global_error[] =
{
148 149 150 151
#include <mysqld_ername.h>
  { 0, 0 }
};

152 153 154 155
static match_err global_expected_errno[MAX_EXPECTED_ERRORS];
static uint global_expected_errors;

/* ************************************************************************ */
unknown's avatar
unknown committed
156

157
static int record = 0, opt_sleep=0;
unknown's avatar
unknown committed
158
static char *db = 0, *pass=0;
159
const char *user = 0, *host = 0, *unix_sock = 0, *opt_basedir="./";
160
static int port = 0;
161
static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0;
162 163 164 165 166
static my_bool tty_password= 0;
static my_bool ps_protocol= 0, ps_protocol_enabled= 0;
static my_bool sp_protocol= 0, sp_protocol_enabled= 0;
static my_bool view_protocol= 0, view_protocol_enabled= 0;
static my_bool cursor_protocol= 0, cursor_protocol_enabled= 0;
167
static int parsing_disabled= 0;
168
const char *manager_user="root",*manager_host=0;
169 170 171 172
char *manager_pass=0;
int manager_port=MYSQL_MANAGER_PORT;
int manager_wait_timeout=3;
MYSQL_MANAGER* manager=0;
173

174
static char **default_argv;
unknown's avatar
unknown committed
175
static const char *load_default_groups[]= { "mysqltest","client",0 };
unknown's avatar
unknown committed
176
static char line_buffer[MAX_DELIMITER], *line_buffer_pos= line_buffer;
unknown's avatar
unknown committed
177

178 179 180 181
typedef struct
{
  FILE* file;
  const char *file_name;
182
  uint lineno; /* Current line in file */
183 184 185 186 187
} test_file;

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

unknown's avatar
unknown committed
190
static char TMPDIR[FN_REFLEN];
191 192
static char delimiter[MAX_DELIMITER]= DEFAULT_DELIMITER;
static uint delimiter_length= 1;
unknown's avatar
unknown committed
193

unknown's avatar
unknown committed
194 195 196 197 198 199 200 201 202 203
/* 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;
204

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

208 209 210
static int embedded_server_arg_count=0;
static char *embedded_server_args[MAX_SERVER_ARGS];

211
static my_bool display_result_vertically= FALSE, display_metadata= FALSE;
212

unknown's avatar
unknown committed
213 214 215 216 217 218 219
/* 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);

220 221 222 223 224 225 226 227
/* Precompiled re's */
static my_regex_t ps_re;     /* the query can be run using PS protocol */
static my_regex_t sp_re;     /* the query can be run as a SP */
static my_regex_t view_re;   /* the query can be run as a view*/

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

229 230
static const char *embedded_server_groups[]=
{
231 232 233 234 235 236
  "server",
  "embedded",
  "mysqltest_SERVER",
  NullS
};

237 238
DYNAMIC_ARRAY q_lines;

unknown's avatar
unknown committed
239 240
#include "sslopt-vars.h"

241
typedef struct
unknown's avatar
unknown committed
242 243 244 245 246
{
  char file[FN_REFLEN];
  ulong pos;
} MASTER_POS ;

unknown's avatar
unknown committed
247 248 249
struct connection
{
  MYSQL mysql;
250 251
  /* Used when creating views and sp, to avoid implicit commit */
  MYSQL* util_mysql;
unknown's avatar
unknown committed
252
  char *name;
253
  MYSQL_STMT* stmt;
unknown's avatar
unknown committed
254 255
};

unknown's avatar
unknown committed
256 257 258 259
typedef struct
{
  int read_lines,current_line;
} PARSER;
260 261

PARSER parser;
unknown's avatar
unknown committed
262
MASTER_POS master_pos;
unknown's avatar
unknown committed
263 264
/* if set, all results are concated and compared against this file */
const char *result_file = 0;
265

266
typedef struct
267
{
unknown's avatar
unknown committed
268
  char *name;
unknown's avatar
unknown committed
269
  int name_len;
unknown's avatar
unknown committed
270
  char *str_val;
271 272 273 274
  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
275
  int alloced;
276 277
} VAR;

unknown's avatar
unknown committed
278
#if defined(__NETWARE__) || defined(__WIN__)
unknown's avatar
unknown committed
279 280 281 282 283 284
/*
  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);
285
static FILE *my_popen(const char *cmd, const char *mode);
286
#undef popen
unknown's avatar
unknown committed
287 288 289
#define popen(A,B) my_popen((A),(B))
#endif /* __NETWARE__ */

290 291
VAR var_reg[10];
/*Perl/shell-like variable registers */
unknown's avatar
unknown committed
292
HASH var_hash;
293
my_bool disable_query_log=0, disable_result_log=0, disable_warnings=0;
294
my_bool disable_ps_warnings= 0;
295
my_bool disable_info= 1;			/* By default off */
296
my_bool abort_on_error= 1;
297

unknown's avatar
unknown committed
298 299 300
struct connection cons[MAX_CONS];
struct connection* cur_con, *next_con, *cons_end;

unknown's avatar
unknown committed
301 302 303
  /* Add new commands before Q_UNKNOWN !*/

enum enum_commands {
304
Q_CONNECTION=1,     Q_QUERY,
unknown's avatar
unknown committed
305
Q_CONNECT,	    Q_SLEEP, Q_REAL_SLEEP,
306 307 308 309 310 311
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,
312 313 314
Q_SYNC_WITH_MASTER,
Q_SYNC_SLAVE_WITH_MASTER,
Q_ERROR,
315
Q_SEND,		    Q_REAP,
316
Q_DIRTY_CLOSE,	    Q_REPLACE, Q_REPLACE_COLUMN,
317 318
Q_PING,		    Q_EVAL,
Q_RPL_PROBE,	    Q_ENABLE_RPL_PARSE,
319
Q_DISABLE_RPL_PARSE, Q_EVAL_RESULT,
unknown's avatar
unknown committed
320
Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
unknown's avatar
unknown committed
321
Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
322
Q_SERVER_START, Q_SERVER_STOP,Q_REQUIRE_MANAGER,
323
Q_WAIT_FOR_SLAVE_TO_STOP,
324
Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
325
Q_ENABLE_PS_WARNINGS, Q_DISABLE_PS_WARNINGS,
326
Q_ENABLE_INFO, Q_DISABLE_INFO,
327
Q_ENABLE_METADATA, Q_DISABLE_METADATA,
328
Q_EXEC, Q_DELIMITER,
329
Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR,
330
Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
unknown's avatar
unknown committed
331
Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL,
unknown's avatar
unknown committed
332
Q_START_TIMER, Q_END_TIMER,
333
Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL,
unknown's avatar
unknown committed
334
Q_EXIT,
335
Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT,
unknown's avatar
unknown committed
336
Q_IF,
337
Q_DISABLE_PARSING, Q_ENABLE_PARSING,
338

339 340
Q_UNKNOWN,			       /* Unknown command.   */
Q_COMMENT,			       /* Comments, ignored. */
341
Q_COMMENT_WITH_COMMAND
unknown's avatar
unknown committed
342 343
};

344
/* this should really be called command */
345
struct st_query
unknown's avatar
unknown committed
346
{
347
  char *query, *query_buf,*first_argument,*last_argument,*end;
unknown's avatar
unknown committed
348
  int first_word_len;
349
  my_bool abort_on_error, require_file;
350
  match_err expected_errno[MAX_EXPECTED_ERRORS];
unknown's avatar
unknown committed
351
  uint expected_errors;
unknown's avatar
unknown committed
352
  char record_file[FN_REFLEN];
unknown's avatar
unknown committed
353
  enum enum_commands type;
unknown's avatar
unknown committed
354 355
};

356 357 358 359 360
const char *command_names[]=
{
  "connection",
  "query",
  "connect",
unknown's avatar
unknown committed
361 362
  /* the difference between sleep and real_sleep is that sleep will use
     the delay from command line (--sleep) if there is one.
363
     real_sleep always uses delay from mysqltest's command line argument.
unknown's avatar
unknown committed
364 365 366 367
     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
   */
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
  "sleep",
  "real_sleep",
  "inc",
  "dec",
  "source",
  "disconnect",
  "let",
  "echo",
  "while",
  "end",
  "system",
  "result",
  "require",
  "save_master_pos",
  "sync_with_master",
383
  "sync_slave_with_master",
384 385 386 387 388
  "error",
  "send",
  "reap",
  "dirty_close",
  "replace_result",
389
  "replace_column",
390 391 392 393 394 395 396 397 398 399 400 401 402 403
  "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",
404 405
  "enable_warnings",
  "disable_warnings",
406 407
  "enable_ps_warnings",
  "disable_ps_warnings",
408
  "enable_info",
409
  "disable_info",
410 411
  "enable_metadata",
  "disable_metadata",
412
  "exec",
413
  "delimiter",
414 415
  "disable_abort_on_error",
  "enable_abort_on_error",
416
  "vertical_results",
417
  "horizontal_results",
418
  "query_vertical",
419
  "query_horizontal",
unknown's avatar
unknown committed
420 421
  "start_timer",
  "end_timer",
unknown's avatar
unknown committed
422
  "character_set",
423 424
  "disable_ps_protocol",
  "enable_ps_protocol",
unknown's avatar
unknown committed
425
  "exit",
426 427
  "disable_reconnect",
  "enable_reconnect",
unknown's avatar
unknown committed
428
  "if",
429 430
  "disable_parsing",
  "enable_parsing",
unknown's avatar
unknown committed
431
  0
432 433 434
};

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

437
DYNAMIC_STRING ds_res;
unknown's avatar
unknown committed
438
static void die(const char *fmt, ...);
unknown's avatar
unknown committed
439
static void init_var_hash();
440
static VAR* var_from_env(const char *, const char *);
441
static byte* get_var_key(const byte* rec, uint* len, my_bool t);
unknown's avatar
unknown committed
442
static VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
unknown's avatar
unknown committed
443 444 445
		     int val_len);

static void var_free(void* v);
446

447
void dump_result_to_reject_file(const char *record_file, char *buf, int size);
unknown's avatar
unknown committed
448

449
int close_connection(struct st_query*);
unknown's avatar
unknown committed
450
static void set_charset(struct st_query*);
451 452
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
453 454
int eval_expr(VAR* v, const char *p, const char** p_end);
static int read_server_arguments(const char *name);
455

456
/* Definitions for replace result */
unknown's avatar
unknown committed
457 458 459 460 461

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. */
462
  uint	array_allocs,max_count,length,max_length;
unknown's avatar
unknown committed
463 464 465 466 467 468
} 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
469
		     uint *max_length, const char *from);
470
void free_replace();
unknown's avatar
unknown committed
471 472 473
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);
474
static void do_eval(DYNAMIC_STRING *query_eval, const char *query);
475
static void str_to_file(const char *fname, char *str, int size);
476
int do_server_op(struct st_query *q,const char *op);
unknown's avatar
unknown committed
477 478 479 480

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

483 484 485 486 487 488 489
/* 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
490
/* Disable functions that only exist in MySQL 4.0 */
unknown's avatar
SCRUM  
unknown committed
491
#if MYSQL_VERSION_ID < 40000
unknown's avatar
unknown committed
492 493 494
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; }
495
my_bool mysql_rpl_probe(MYSQL *mysql __attribute__((unused))) { return 1; }
unknown's avatar
unknown committed
496
#endif
497 498
static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
				      int len);
499
static void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val);
500 501 502 503
static void handle_error(const char *query, struct st_query *q,
			 unsigned int err_errno, const char *err_error,
			 const char *err_sqlstate, DYNAMIC_STRING *ds);
static void handle_no_error(struct st_query *q);
504

505
static void do_eval(DYNAMIC_STRING* query_eval, const char *query)
506
{
507
  const char *p;
508 509 510
  register char c;
  register int escaped = 0;
  VAR* v;
unknown's avatar
unknown committed
511
  DBUG_ENTER("do_eval");
unknown's avatar
unknown committed
512

513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
  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;
541
    }
542
  }
unknown's avatar
unknown committed
543
  DBUG_VOID_RETURN;
544
}
unknown's avatar
unknown committed
545

546

547 548
static void close_cons()
{
549 550 551
  DBUG_ENTER("close_cons");
  for (--next_con; next_con >= cons; --next_con)
  {
552 553 554
    if (next_con->stmt)
      mysql_stmt_close(next_con->stmt);
    next_con->stmt= 0;
555
    mysql_close(&next_con->mysql);
556 557
    if (next_con->util_mysql)
      mysql_close(next_con->util_mysql);
558 559 560 561 562
    my_free(next_con->name, MYF(MY_ALLOW_ZERO_PTR));
  }
  DBUG_VOID_RETURN;
}

563

564 565
static void close_files()
{
unknown's avatar
unknown committed
566
  DBUG_ENTER("close_files");
567
  for (; cur_file >= file_stack; cur_file--)
568
  {
569 570 571 572 573
    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
574 575
  }
  DBUG_VOID_RETURN;
576 577
}

578

579 580 581 582
static void free_used_memory()
{
  uint i;
  DBUG_ENTER("free_used_memory");
583
#ifndef EMBEDDED_LIBRARY
584 585
  if (manager)
    mysql_manager_close(manager);
586
#endif
587 588
  close_cons();
  close_files();
unknown's avatar
unknown committed
589
  hash_free(&var_hash);
unknown's avatar
unknown committed
590

591 592 593
  for (i=0 ; i < q_lines.elements ; i++)
  {
    struct st_query **q= dynamic_element(&q_lines, i, struct st_query**);
594
    my_free((gptr) (*q)->query_buf,MYF(MY_ALLOW_ZERO_PTR));
595 596
    my_free((gptr) (*q),MYF(0));
  }
597
  for (i=0; i < 10; i++)
unknown's avatar
unknown committed
598 599 600 601
  {
    if (var_reg[i].alloced_len)
      my_free(var_reg[i].str_val, MYF(MY_WME));
  }
602 603
  while (embedded_server_arg_count > 1)
    my_free(embedded_server_args[--embedded_server_arg_count],MYF(0));
604 605
  delete_dynamic(&q_lines);
  dynstr_free(&ds_res);
unknown's avatar
unknown committed
606
  free_replace();
607
  free_replace_column();
608 609
  my_free(pass,MYF(MY_ALLOW_ZERO_PTR));
  free_defaults(default_argv);
unknown's avatar
unknown committed
610
  mysql_server_end();
611
  free_re();
612
  DBUG_VOID_RETURN;
613 614
}

615
static void die(const char *fmt, ...)
616 617
{
  va_list args;
618
  DBUG_ENTER("die");
619
  va_start(args, fmt);
620 621
  if (fmt)
  {
622 623
    fprintf(stderr, "mysqltest: ");
    if (cur_file && cur_file != file_stack)
624
      fprintf(stderr, "In included file \"%s\": ",
625
              cur_file->file_name);
unknown's avatar
unknown committed
626 627
    if (start_lineno != 0)
      fprintf(stderr, "At line %u: ", start_lineno);
628 629
    vfprintf(stderr, fmt, args);
    fprintf(stderr, "\n");
unknown's avatar
unknown committed
630
    fflush(stderr);
631
  }
632
  va_end(args);
633
  free_used_memory();
634
  my_end(MY_CHECK_ERROR);
unknown's avatar
unknown committed
635 636 637 638

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

639 640 641
  exit(1);
}

642 643
/* Note that we will get some memory leaks when calling this! */

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

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

  va_start(args, fmt);

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

unknown's avatar
unknown committed
673

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


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

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

  if ((fd = my_open(eval_file, O_RDONLY, MYF(MY_WME))) < 0)
713
    die(NullS);
714
  if (my_read(fd, (byte*)tmp, stat_info.st_size, MYF(MY_WME|MY_NABP)))
715
    die(NullS);
716 717 718 719 720 721
  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;
722
    if ((res_len = res_ds.length) != ds->length)
723
    {
724
      res= RESULT_LENGTH_MISMATCH;
725 726 727 728 729 730 731 732
      goto err;
    }
  }
  else
  {
    res_ptr = tmp;
    res_len = stat_info.st_size;
  }
unknown's avatar
unknown committed
733

734 735
  res= (memcmp(res_ptr, ds->str, res_len)) ?
    RESULT_CONTENT_MISMATCH : RESULT_OK;
unknown's avatar
unknown committed
736

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

742 743
  my_free((gptr) tmp, MYF(0));
  my_close(fd, MYF(MY_WME));
744
  dynstr_free(&res_ds);
unknown's avatar
unknown committed
745

746
  DBUG_RETURN(res);
unknown's avatar
unknown committed
747 748
}

749 750
/*
  Check the content of ds against content of file fname
unknown's avatar
unknown committed
751

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

759 760 761 762 763
  RETURN VALUES
   error - the function will not return

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

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

  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
787 788
}

789

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

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

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

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

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

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

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

881

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

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

907 908 909 910 911 912 913 914

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

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

  /* Check for extra delimiter */
923
  if (*p && !strncmp(p, delimiter, delimiter_length))
924
    die("Extra delimiter \"%s\" found", delimiter);
925

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

unknown's avatar
unknown committed
936

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

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

966
int do_require_manager(struct st_query *query __attribute__((unused)) )
967 968 969 970 971 972
{
  if (!manager)
    abort_not_supported_test();
  return 0;
}

973
#ifndef EMBEDDED_LIBRARY
974
int do_server_start(struct st_query *q)
975
{
976
  return do_server_op(q, "start");
977 978
}

979
int do_server_stop(struct st_query *q)
980
{
981
  return do_server_op(q, "stop");
982 983
}

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

1011
  q->last_argument= p;
1012 1013
  return 0;
}
1014
#endif
1015

1016 1017 1018 1019 1020 1021

/*
  Source and execute the given file

  SYNOPSIS
    do_source()
1022
    query	called command
1023 1024 1025 1026 1027 1028 1029 1030

  DESCRIPTION
    source <file_name>

    Open the file <file_name> and execute it

*/

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

1051

1052 1053 1054 1055 1056
/*
  Execute given command.

  SYNOPSIS
    do_exec()
1057
    query	called command
1058 1059

  DESCRIPTION
1060 1061 1062 1063 1064 1065
    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.
1066 1067 1068

*/

1069
static void do_exec(struct st_query *query)
1070
{
unknown's avatar
unknown committed
1071
  int error;
1072
  DYNAMIC_STRING *ds= NULL;
1073 1074
  char buf[1024];
  FILE *res_file;
1075
  char *cmd= query->first_argument;
unknown's avatar
unknown committed
1076
  DBUG_ENTER("do_exec");
1077

unknown's avatar
unknown committed
1078
  while (*cmd && my_isspace(charset_info, *cmd))
1079 1080
    cmd++;
  if (!*cmd)
1081
    die("Missing argument in exec");
1082
  query->last_argument= query->end;
1083

unknown's avatar
unknown committed
1084 1085
  DBUG_PRINT("info", ("Executing '%s'", cmd));

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

1089
  if (disable_result_log)
1090
  {
unknown's avatar
unknown committed
1091
    while (fgets(buf, sizeof(buf), res_file))
1092 1093 1094 1095
    {
      buf[strlen(buf)-1]=0;
      DBUG_PRINT("exec_result",("%s", buf));
    }
1096 1097
  }
  else
1098
  {
1099
    ds= &ds_res;
1100
    while (fgets(buf, sizeof(buf), res_file))
1101
      replace_dynstr_append(ds, buf);
unknown's avatar
unknown committed
1102 1103 1104
  }
  error= pclose(res_file);
  if (error != 0)
unknown's avatar
patch  
unknown committed
1105
  {
unknown's avatar
unknown committed
1106 1107 1108
    uint status= WEXITSTATUS(error), i;
    my_bool ok= 0;

1109
    if (query->abort_on_error)
1110
      die("command \"%s\" failed", cmd);
unknown's avatar
unknown committed
1111 1112 1113

    DBUG_PRINT("info",
               ("error: %d, status: %d", error, status));
1114
    for (i= 0; i < query->expected_errors; i++)
unknown's avatar
patch  
unknown committed
1115
    {
1116 1117
      DBUG_PRINT("info",
                 ("error: %d, status: %d", error, status));
unknown's avatar
unknown committed
1118
      DBUG_PRINT("info", ("expected error: %d",
1119 1120 1121
                          query->expected_errno[i].code.errnum));
      if ((query->expected_errno[i].type == ERR_ERRNO) &&
          (query->expected_errno[i].code.errnum == status))
1122
      {
unknown's avatar
unknown committed
1123
        ok= 1;
1124 1125
        DBUG_PRINT("info", ("command \"%s\" failed with expected error: %d",
                            cmd, status));
1126
      }
unknown's avatar
patch  
unknown committed
1127
    }
unknown's avatar
unknown committed
1128
    if (!ok)
1129 1130
      die("command \"%s\" failed with wrong error: %d",
          cmd, status);
unknown's avatar
patch  
unknown committed
1131
  }
1132 1133
  else if (query->expected_errno[0].type == ERR_ERRNO &&
           query->expected_errno[0].code.errnum != 0)
unknown's avatar
patch  
unknown committed
1134 1135
  {
    /* Error code we wanted was != 0, i.e. not an expected success */
1136
    die("command \"%s\" succeeded - should have failed with errno %d...",
1137
        cmd, query->expected_errno[0].code.errnum);
unknown's avatar
patch  
unknown committed
1138
  }
unknown's avatar
unknown committed
1139

1140
  free_replace();
1141
  DBUG_VOID_RETURN;
1142 1143
}

1144 1145 1146 1147 1148 1149 1150 1151
/*
  Set variable from the result of a query

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

1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163

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

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

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

1165 1166 1167
*/

int var_query_set(VAR* var, const char *query, const char** query_end)
1168
{
1169 1170
  char* end = (char*)((query_end && *query_end) ?
		      *query_end : query + strlen(query));
1171 1172 1173 1174
  MYSQL_RES *res;
  MYSQL_ROW row;
  MYSQL* mysql = &cur_con->mysql;
  LINT_INIT(res);
unknown's avatar
unknown committed
1175

1176
  while (end > query && *end != '`')
1177
    --end;
1178
  if (query == end)
1179
    die("Syntax error in query, missing '`'");
1180
  ++query;
1181

1182
  if (mysql_real_query(mysql, query, (int)(end - query)) ||
1183 1184 1185
      !(res = mysql_store_result(mysql)))
  {
    *end = 0;
1186 1187
    die("Error running query '%s': %d: %s", query,
	mysql_errno(mysql) ,mysql_error(mysql));
1188 1189 1190
  }

  if ((row = mysql_fetch_row(res)) && row[0])
1191 1192 1193 1194 1195 1196 1197 1198 1199
  {
    /*
      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;
1200
    MYSQL_FIELD *fields= mysql_fetch_fields(res);
1201 1202 1203 1204 1205 1206

    init_dynamic_string(&result, "", 16384, 65536);
    lengths= mysql_fetch_lengths(res);
    for (i=0; i < mysql_num_fields(res); i++)
    {
      if (row[0])
1207
      {
1208
#ifdef NOT_YET
1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221
	/* Add to <var_name>_<col_name> */
	uint j;
	char var_col_name[MAX_VAR_NAME];
	uint length= snprintf(var_col_name, MAX_VAR_NAME,
			      "$%s_%s", var->name, fields[i].name);
	/* Convert characters not allowed in variable names to '_' */
	for (j= 1; j < length; j++)
	{
	  if (!my_isvar(charset_info,var_col_name[j]))
	     var_col_name[j]= '_';
        }
	var_set(var_col_name,  var_col_name + length,
		row[i], row[i] + lengths[i]);
1222
#endif
1223
        /* Add column to tab separated string */
1224
	dynstr_append_mem(&result, row[i], lengths[i]);
1225
      }
1226 1227 1228
      dynstr_append_mem(&result, "\t", 1);
    }
    end= result.str + result.length-1;
1229
    eval_expr(var, result.str, (const char**) &end);
1230 1231
    dynstr_free(&result);
  }
1232
  else
1233
    eval_expr(var, "", 0);
1234 1235 1236 1237

  mysql_free_result(res);
  return 0;
}
1238

1239
void var_copy(VAR *dest, VAR *src)
unknown's avatar
unknown committed
1240
{
1241 1242 1243 1244
  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
1245
  if (dest->alloced_len < src->alloced_len &&
1246 1247 1248
      !(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
1249
    die("Out of memory");
1250 1251 1252 1253 1254 1255 1256
  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
1257 1258
}

1259
int eval_expr(VAR* v, const char *p, const char** p_end)
1260 1261
{
  VAR* vp;
1262
  if (*p == '$')
1263 1264
  {
    if ((vp = var_get(p,p_end,0,0)))
1265
    {
1266 1267
      var_copy(v, vp);
      return 0;
unknown's avatar
unknown committed
1268
    }
1269
  }
1270
  else if (*p == '`')
1271 1272 1273
  {
    return var_query_set(v, p, p_end);
  }
unknown's avatar
unknown committed
1274 1275
  else
    {
1276 1277 1278 1279
      int new_val_len = (p_end && *p_end) ?
	 (int) (*p_end - p) : (int) strlen(p);
      if (new_val_len + 1 >= v->alloced_len)
      {
1280
	v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
1281 1282
	  MIN_VAR_ALLOC : new_val_len + 1;
	if (!(v->str_val =
1283
	      v->str_val ? my_realloc(v->str_val, v->alloced_len+1,
1284
				      MYF(MY_WME)) :
1285
	      my_malloc(v->alloced_len+1, MYF(MY_WME))))
1286 1287 1288 1289 1290
	  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
1291 1292
      v->int_val=atoi(p);
      v->int_dirty=0;
1293 1294
      return 0;
    }
1295

1296 1297 1298 1299
  die("Invalid expr: %s", p);
  return 1;
}

1300

1301
enum enum_operator
1302
{
1303 1304 1305
  DO_DEC,
  DO_INC
};
1306

1307
/*
1308
  Decrease or increase the value of a variable
1309 1310

  SYNOPSIS
1311 1312 1313 1314
    do_modify_var()
    query	called command
    name        human readable name of operator
    operator    operation to perform on the var
1315 1316 1317

  DESCRIPTION
    dec $var_name
1318
    inc $var_name
1319

1320 1321
*/

1322 1323
int do_modify_var(struct st_query *query, const char *name,
                  enum enum_operator operator)
1324
{
1325
  const char *p= query->first_argument;
1326
  VAR* v;
1327
  if (!*p)
1328
    die("Missing arguments to %s", name);
1329
  if (*p != '$')
1330 1331
    die("First argument to %s must be a variable (start with $)", name);
  v= var_get(p, &p, 1, 0);
1332
  switch (operator) {
1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343
  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;
1344
  query->last_argument= (char*)++p;
1345 1346 1347
  return 0;
}

1348

1349
int do_system(struct st_query *q)
unknown's avatar
unknown committed
1350
{
1351
  DYNAMIC_STRING *ds;
1352
  char *p=q->first_argument;
unknown's avatar
unknown committed
1353
  VAR v;
1354
  var_init(&v, 0, 0, 0, 0);
unknown's avatar
unknown committed
1355
  eval_expr(&v, p, 0); /* NULL terminated */
1356 1357
  ds= &ds_res;

unknown's avatar
unknown committed
1358
  if (v.str_val_len)
unknown's avatar
unknown committed
1359
  {
unknown's avatar
unknown committed
1360
    char expr_buf[1024];
unknown's avatar
unknown committed
1361 1362 1363 1364 1365
    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));
1366 1367 1368 1369
    if (system(expr_buf))
    {
      if (q->abort_on_error)
        die("system command '%s' failed", expr_buf);
1370 1371 1372 1373 1374

      /* If ! abort_on_error, log message and continue */
      dynstr_append(ds, "system command '");
      replace_dynstr_append(ds, expr_buf);
      dynstr_append(ds, "' failed\n");
1375
    }
unknown's avatar
unknown committed
1376
  }
1377 1378
  else
    die("Missing arguments to system, nothing to do!");
unknown's avatar
unknown committed
1379
  var_free(&v);
1380
  q->last_argument= q->end;
unknown's avatar
unknown committed
1381 1382
  return 0;
}
1383

1384 1385 1386

/*
  Print the content between echo and <delimiter> to result file.
1387 1388
  Evaluate all variables in the string before printing, allow
  for variable names to be escaped using \
1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400

  SYNOPSIS
    do_echo()
    q  called command

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

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

1401 1402 1403 1404 1405 1406
    echo Some text $<var_name>
    Print "Some text" plus the content of the variable <var_name> to
    result file

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

1409
int do_echo(struct st_query *command)
1410
{
1411
  DYNAMIC_STRING *ds, ds_echo;
1412

1413
  ds= &ds_res;
1414

1415 1416 1417
  init_dynamic_string(&ds_echo, "", 256, 256);
  do_eval(&ds_echo, command->first_argument);
  dynstr_append_mem(ds, ds_echo.str, ds_echo.length);
1418
  dynstr_append_mem(ds, "\n", 1);
1419 1420
  dynstr_free(&ds_echo);
  command->last_argument= command->end;
1421 1422 1423
  return 0;
}

1424

1425
int do_sync_with_master2(long offset)
unknown's avatar
unknown committed
1426 1427 1428
{
  MYSQL_RES* res;
  MYSQL_ROW row;
1429
  MYSQL* mysql= &cur_con->mysql;
unknown's avatar
unknown committed
1430
  char query_buf[FN_REFLEN+128];
1431
  int tries= 0;
1432 1433
  int rpl_parse;

1434
  if (!master_pos.file[0])
unknown's avatar
unknown committed
1435
    die("Calling 'sync_with_master' without calling 'save_master_pos'");
1436
  rpl_parse= mysql_rpl_parse_enabled(mysql);
1437
  mysql_disable_rpl_parse(mysql);
unknown's avatar
unknown committed
1438

unknown's avatar
unknown committed
1439
  sprintf(query_buf, "select master_pos_wait('%s', %ld)", master_pos.file,
1440
	  master_pos.pos + offset);
1441 1442 1443

wait_for_position:

1444
  if (mysql_query(mysql, query_buf))
unknown's avatar
unknown committed
1445 1446
    die("failed in %s: %d: %s", query_buf, mysql_errno(mysql),
        mysql_error(mysql));
unknown's avatar
unknown committed
1447

1448
  if (!(res= mysql_store_result(mysql)))
unknown's avatar
unknown committed
1449
    die("mysql_store_result() returned NULL for '%s'", query_buf);
1450
  if (!(row= mysql_fetch_row(res)))
unknown's avatar
unknown committed
1451
    die("empty result in %s", query_buf);
1452
  if (!row[0])
1453 1454 1455 1456 1457 1458
  {
    /*
      It may be that the slave SQL thread has not started yet, though START
      SLAVE has been issued ?
    */
    if (tries++ == 3)
unknown's avatar
unknown committed
1459
      die("could not sync with master ('%s' returned NULL)", query_buf);
1460 1461 1462 1463
    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
1464
  mysql_free_result(res);
1465
  if (rpl_parse)
1466
    mysql_enable_rpl_parse(mysql);
unknown's avatar
unknown committed
1467

unknown's avatar
unknown committed
1468 1469 1470
  return 0;
}

1471
int do_sync_with_master(struct st_query *query)
1472
{
1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485
  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);
1486
}
1487

unknown's avatar
unknown committed
1488 1489 1490 1491 1492
int do_save_master_pos()
{
  MYSQL_RES* res;
  MYSQL_ROW row;
  MYSQL* mysql = &cur_con->mysql;
1493
  const char *query;
1494 1495 1496 1497
  int rpl_parse;

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

1499
  if (mysql_query(mysql, query= "show master status"))
1500
    die("failed in show master status: %d: %s",
unknown's avatar
unknown committed
1501 1502
	mysql_errno(mysql), mysql_error(mysql));

1503
  if (!(res = mysql_store_result(mysql)))
unknown's avatar
unknown committed
1504
    die("mysql_store_result() retuned NULL for '%s'", query);
1505
  if (!(row = mysql_fetch_row(res)))
unknown's avatar
unknown committed
1506
    die("empty result in show master status");
1507 1508
  strnmov(master_pos.file, row[0], sizeof(master_pos.file)-1);
  master_pos.pos = strtoul(row[1], (char**) 0, 10);
1509
  mysql_free_result(res);
unknown's avatar
unknown committed
1510

1511
  if (rpl_parse)
1512
    mysql_enable_rpl_parse(mysql);
unknown's avatar
unknown committed
1513

unknown's avatar
unknown committed
1514 1515 1516 1517
  return 0;
}


1518 1519 1520 1521 1522
/*
  Assign the variable <var_name> with <var_val>

  SYNOPSIS
   do_let()
1523
    query	called command
1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536

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

1537
int do_let(struct st_query *query)
1538
{
1539
  char *p= query->first_argument;
1540
  char *var_name, *var_name_end, *var_val_start;
1541 1542

  /* Find <var_name> */
1543
  if (!*p)
1544
    die("Missing arguments to let");
1545 1546
  var_name= p;
  while (*p && (*p != '=') && !my_isspace(charset_info,*p))
1547
    p++;
1548 1549
  var_name_end= p;
  if (var_name+1 == var_name_end)
1550
    die("Missing variable name in let");
1551
  while (my_isspace(charset_info,*p))
1552
    p++;
1553
  if (*p++ != '=')
1554 1555 1556
    die("Missing assignment operator in let");

  /* Find start of <var_val> */
1557
  while (*p && my_isspace(charset_info,*p))
1558
    p++;
1559 1560
  var_val_start= p;
  query->last_argument= query->end;
1561
  /* Assign var_val to var_name */
1562
  return var_set(var_name, var_name_end, var_val_start, query->end);
1563 1564
}

1565 1566 1567 1568 1569 1570 1571 1572

/*
  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)
1573
{
1574 1575 1576 1577
  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);
1578 1579
}

1580

1581
int do_rpl_probe(struct st_query *query __attribute__((unused)))
1582
{
unknown's avatar
unknown committed
1583
  DBUG_ENTER("do_rpl_probe");
1584
  if (mysql_rpl_probe(&cur_con->mysql))
unknown's avatar
unknown committed
1585 1586
    die("Failed in mysql_rpl_probe(): '%s'", mysql_error(&cur_con->mysql));
  DBUG_RETURN(0);
1587 1588
}

1589

1590
int do_enable_rpl_parse(struct st_query *query __attribute__((unused)))
1591 1592 1593 1594 1595
{
  mysql_enable_rpl_parse(&cur_con->mysql);
  return 0;
}

1596

1597
int do_disable_rpl_parse(struct st_query *query __attribute__((unused)))
1598 1599 1600 1601 1602 1603
{
  mysql_disable_rpl_parse(&cur_con->mysql);
  return 0;
}


1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617
/*
  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

*/

1618
int do_sleep(struct st_query *query, my_bool real_sleep)
unknown's avatar
unknown committed
1619
{
1620 1621 1622 1623 1624 1625
  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
1626
    p++;
1627
  if (!*p)
1628
    die("Missing argument to sleep");
1629 1630 1631 1632 1633 1634 1635 1636 1637
  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 */
1638
  if (opt_sleep && !real_sleep)
1639 1640
    sleep_val= opt_sleep;

1641
  DBUG_PRINT("info", ("sleep_val: %f", sleep_val));
1642 1643
  my_sleep((ulong) (sleep_val * 1000000L));
  query->last_argument= sleep_end;
unknown's avatar
unknown committed
1644
  return 0;
unknown's avatar
unknown committed
1645 1646
}

1647
static void get_file_name(char *filename, struct st_query *q)
1648
{
1649
  char *p= q->first_argument, *name;
1650 1651
  if (!*p)
    die("Missing file name argument");
1652
  name= p;
1653 1654 1655 1656
  while (*p && !my_isspace(charset_info,*p))
    p++;
  if (*p)
    *p++= 0;
1657
  q->last_argument= p;
1658
  strmake(filename, name, FN_REFLEN);
1659 1660
}

1661
static void set_charset(struct st_query *q)
unknown's avatar
unknown committed
1662
{
1663 1664
  char *charset_name= q->first_argument;
  char *p;
unknown's avatar
unknown committed
1665 1666

  if (!charset_name || !*charset_name)
1667
    die("Missing charset name in 'character_set'");
unknown's avatar
unknown committed
1668
  /* Remove end space */
1669 1670 1671 1672 1673
  p= charset_name;
  while (*p && !my_isspace(charset_info,*p))
    p++;
  if(*p)
    *p++= 0;
1674
  q->last_argument= p;
unknown's avatar
unknown committed
1675 1676 1677 1678
  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
1679

1680
static uint get_errcodes(match_err *to,struct st_query *q)
unknown's avatar
unknown committed
1681
{
1682
  char *p= q->first_argument;
1683
  uint count= 0;
1684

1685
  DBUG_ENTER("get_errcodes");
1686

unknown's avatar
unknown committed
1687
  if (!*p)
1688
    die("Missing argument in %s", q->query);
1689

1690
  do
1691
  {
1692 1693 1694
    if (*p == 'S')
    {
      /* SQLSTATE string */
1695 1696 1697 1698 1699 1700 1701
      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;

1702 1703
      to[count].type= ERR_SQLSTATE;
    }
1704 1705 1706 1707 1708
    else if (*p == 'E')
    {
      /* SQL error as string */
      st_error *e= global_error;
      char *start= p++;
unknown's avatar
unknown committed
1709

1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721
      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)
1722
	die("Unknown SQL error '%s'", start);
1723
    }
1724 1725 1726
    else
    {
      long val;
1727 1728

      if (!(p= str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val)))
1729
	die("Invalid argument in %s", q->query);
1730 1731 1732
      to[count].code.errnum= (uint) val;
      to[count].type= ERR_ERRNO;
    }
unknown's avatar
unknown committed
1733
    count++;
1734
  } while (*(p++) == ',');
1735
  q->last_argument= (p - 1);
1736
  to[count].type= ERR_EMPTY;                        /* End of data */
unknown's avatar
unknown committed
1737
  DBUG_RETURN(count);
unknown's avatar
unknown committed
1738 1739
}

unknown's avatar
unknown committed
1740 1741 1742
/*
  Get a string;  Return ptr to end of string
  Strings may be surrounded by " or '
1743 1744

  If string is a '$variable', return the value of the variable.
unknown's avatar
unknown committed
1745 1746 1747
*/


1748
static char *get_string(char **to_ptr, char **from_ptr,
1749
			struct st_query *q)
unknown's avatar
unknown committed
1750 1751
{
  reg1 char c,sep;
1752
  char *to= *to_ptr, *from= *from_ptr, *start=to;
unknown's avatar
unknown committed
1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796
  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)
1797
    die("Wrong string argument in %s", q->query);
unknown's avatar
unknown committed
1798

1799
  while (my_isspace(charset_info,*from))	/* Point to next string */
unknown's avatar
unknown committed
1800 1801
    from++;

1802 1803
  *to =0;				/* End of string marker */
  *to_ptr= to+1;			/* Store pointer to end */
unknown's avatar
unknown committed
1804
  *from_ptr= from;
1805 1806 1807 1808 1809 1810 1811 1812

  /* 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)
    {
1813
      DBUG_PRINT("info",("var: '%s' -> '%s'", start, var->str_val));
1814 1815 1816 1817
      DBUG_RETURN(var->str_val);	/* return found variable value */
    }
  }
  DBUG_RETURN(start);
unknown's avatar
unknown committed
1818 1819 1820 1821 1822 1823 1824
}


/*
  Get arguments for replace. The syntax is:
  replace from to [from to ...]
  Where each argument may be quoted with ' or "
1825 1826
  A argument may also be a variable, in which case the value of the
  variable is replaced.
unknown's avatar
unknown committed
1827 1828 1829 1830 1831
*/

static void get_replace(struct st_query *q)
{
  uint i;
1832
  char *from= q->first_argument;
1833
  char *buff,*start;
unknown's avatar
unknown committed
1834 1835 1836 1837
  char word_end_chars[256],*pos;
  POINTER_ARRAY to_array,from_array;
  DBUG_ENTER("get_replace");

unknown's avatar
unknown committed
1838
  free_replace();
1839

unknown's avatar
unknown committed
1840 1841 1842
  bzero((char*) &to_array,sizeof(to_array));
  bzero((char*) &from_array,sizeof(from_array));
  if (!*from)
1843
    die("Missing argument in %s", q->query);
1844
  start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
unknown's avatar
unknown committed
1845 1846 1847
  while (*from)
  {
    char *to=buff;
1848
    to=get_string(&buff, &from, q);
unknown's avatar
unknown committed
1849
    if (!*from)
1850
      die("Wrong number of arguments to replace_result in '%s'", q->query);
unknown's avatar
unknown committed
1851
    insert_pointer_name(&from_array,to);
1852
    to=get_string(&buff, &from, q);
unknown's avatar
unknown committed
1853 1854 1855
    insert_pointer_name(&to_array,to);
  }
  for (i=1,pos=word_end_chars ; i < 256 ; i++)
1856
    if (my_isspace(charset_info,i))
unknown's avatar
unknown committed
1857
      *pos++= i;
1858
  *pos=0;					/* End pointer */
unknown's avatar
unknown committed
1859 1860 1861 1862 1863
  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())
1864
    die("Can't initialize replace from '%s'", q->query);
unknown's avatar
unknown committed
1865 1866
  free_pointer_array(&from_array);
  free_pointer_array(&to_array);
1867
  my_free(start, MYF(0));
1868
  q->last_argument= q->end;
1869
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1870 1871 1872 1873
}

void free_replace()
{
1874
  DBUG_ENTER("free_replace");
unknown's avatar
unknown committed
1875 1876 1877 1878
  if (glob_replace)
  {
    my_free((char*) glob_replace,MYF(0));
    glob_replace=0;
1879
    my_free(out_buff,MYF(MY_WME));
unknown's avatar
unknown committed
1880
  }
1881
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1882 1883
}

unknown's avatar
unknown committed
1884
struct connection * find_connection_by_name(const char *name)
unknown's avatar
unknown committed
1885 1886
{
  struct connection *con;
1887
  for (con= cons; con < next_con; con++)
1888
  {
1889
    if (!strcmp(con->name, name))
1890
    {
unknown's avatar
unknown committed
1891
      return con;
1892 1893
    }
  }
unknown's avatar
unknown committed
1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905
  return 0; /* Connection not found */
}


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

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

1908 1909

int select_connection(struct st_query *query)
1910
{
1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927
  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)
1928
{
1929
  char *p= q->first_argument, *name;
1930
  struct connection *con;
1931 1932 1933
  DBUG_ENTER("close_connection");
  DBUG_PRINT("enter",("name: '%s'",p));

1934
  if (!*p)
unknown's avatar
unknown committed
1935
    die("Missing connection name in disconnect");
1936
  name= p;
1937
  while (*p && !my_isspace(charset_info,*p))
1938
    p++;
1939

1940 1941
  if (*p)
    *p++= 0;
1942 1943
  q->last_argument= p;
  for (con= cons; con < next_con; con++)
1944
  {
1945
    if (!strcmp(con->name, name))
1946
    {
1947
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
1948 1949 1950
      if (q->type == Q_DIRTY_CLOSE)
      {
	if (con->mysql.net.vio)
unknown's avatar
unknown committed
1951
	{
unknown's avatar
unknown committed
1952 1953
	  vio_delete(con->mysql.net.vio);
	  con->mysql.net.vio = 0;
unknown's avatar
unknown committed
1954
	}
unknown's avatar
unknown committed
1955
      }
1956
#endif
1957
      mysql_close(&con->mysql);
1958 1959 1960
      if (con->util_mysql)
	mysql_close(con->util_mysql);
      con->util_mysql= 0;
unknown's avatar
unknown committed
1961 1962 1963 1964 1965 1966 1967 1968
      my_free(con->name, MYF(0));
      /*
         When the connection is closed set name to "closed_connection"
         to make it possible to reuse the connection name.
         The connection slot will not be reused
       */
      if (!(con->name = my_strdup("closed_connection", MYF(MY_WME))))
        die("Out of memory");
1969 1970 1971
      DBUG_RETURN(0);
    }
  }
1972
  die("connection '%s' not found in connection pool", name);
1973
  DBUG_RETURN(1);				/* Never reached */
1974 1975
}

unknown's avatar
unknown committed
1976

1977 1978
/*
   This one now is a hack - we may want to improve in in the
unknown's avatar
unknown committed
1979 1980 1981
   future to handle quotes. For now we assume that anything that is not
   a comma, a space or ) belongs to the argument. space is a chopper, comma or
   ) are delimiters/terminators
unknown's avatar
unknown committed
1982 1983 1984 1985

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

unknown's avatar
unknown committed
1990 1991
  RETURNS
  pointer to str after param
unknown's avatar
unknown committed
1992

1993
*/
1994

unknown's avatar
unknown committed
1995
char* safe_get_param(char *str, char** arg, const char *msg)
unknown's avatar
unknown committed
1996
{
1997
  DBUG_ENTER("safe_get_param");
unknown's avatar
unknown committed
1998 1999
  if(!*str)
  {
unknown's avatar
unknown committed
2000
    if (msg)
unknown's avatar
unknown committed
2001 2002 2003 2004
      die(msg);
    *arg= str;
    DBUG_RETURN(str);
  }
2005
  while (*str && my_isspace(charset_info,*str))
unknown's avatar
unknown committed
2006
    str++;
2007
  *arg= str;
unknown's avatar
unknown committed
2008 2009
  while (*str && *str != ',' && *str != ')')
    str++;
unknown's avatar
unknown committed
2010
  if (msg && !*arg)
unknown's avatar
unknown committed
2011
    die(msg);
2012

2013
  *str++= 0;
2014
  DBUG_RETURN(str);
unknown's avatar
unknown committed
2015 2016
}

2017
#ifndef EMBEDDED_LIBRARY
2018 2019 2020 2021 2022 2023 2024 2025 2026 2027
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);

}
2028
#endif
2029

2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052

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

2053 2054
int safe_connect(MYSQL* mysql, const char *host, const char *user,
		 const char *pass, const char *db, int port, const char *sock)
unknown's avatar
unknown committed
2055
{
2056 2057
  int con_error= 1;
  my_bool reconnect= 1;
unknown's avatar
unknown committed
2058
  int i;
2059
  for (i= 0; i < MAX_CON_TRIES; ++i)
unknown's avatar
unknown committed
2060
  {
2061
    if (mysql_real_connect(mysql, host,user, pass, db, port, sock,
2062
			   CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS))
unknown's avatar
unknown committed
2063
    {
2064
      con_error= 0;
unknown's avatar
unknown committed
2065 2066 2067 2068
      break;
    }
    sleep(CON_RETRY_SLEEP);
  }
2069 2070 2071 2072
  /*
   TODO: change this to 0 in future versions, but the 'kill' test relies on
   existing behavior
  */
2073
  mysql_options(mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
unknown's avatar
unknown committed
2074 2075 2076
  return con_error;
}

2077

2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105
/*
  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)
{
2106
  DYNAMIC_STRING *ds;
2107
  my_bool reconnect= 1;
2108 2109
  int error= 0;

2110
  ds= &ds_res;
2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142

  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))
  {
2143 2144
    handle_error("connect", q, mysql_errno(con), mysql_error(con),
		 mysql_sqlstate(con), ds);
2145 2146 2147
    *create_conn= 0;
    goto err;
  }
unknown's avatar
unknown committed
2148
  else
2149
  {
2150 2151
    handle_no_error(q);

2152 2153 2154 2155 2156 2157 2158 2159 2160 2161
    /*
      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;
  }

2162 2163 2164 2165
  /*
   TODO: change this to 0 in future versions, but the 'kill' test relies on
   existing behavior
  */
2166
  mysql_options(con, MYSQL_OPT_RECONNECT, (char *)&reconnect);
2167

2168 2169 2170 2171 2172 2173
err:
  free_replace();
  return error;
}


unknown's avatar
unknown committed
2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197
/*
  Open a new connection to MySQL Server with the parameters
  specified

  SYNOPSIS
   do_connect()
    q	       called command

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

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

 */

2198
int do_connect(struct st_query *q)
unknown's avatar
unknown committed
2199
{
2200
  char *con_name, *con_user,*con_pass, *con_host, *con_port_str,
unknown's avatar
unknown committed
2201 2202
    *con_db, *con_sock, *con_options;
  char *con_buf, *p;
2203
  char buff[FN_REFLEN];
unknown's avatar
unknown committed
2204
  int con_port;
unknown's avatar
unknown committed
2205 2206
  bool con_ssl= 0;
  bool con_compress= 0;
2207
  int free_con_sock= 0;
2208 2209
  int error= 0;
  int create_conn= 1;
2210
  VAR *var_port, *var_sock;
unknown's avatar
unknown committed
2211

2212
  DBUG_ENTER("do_connect");
2213
  DBUG_PRINT("enter",("connect: %s", q->first_argument));
unknown's avatar
unknown committed
2214

unknown's avatar
unknown committed
2215 2216 2217 2218 2219
  /* Make a copy of query before parsing, safe_get_param will modify */
  if (!(con_buf= my_strdup(q->first_argument, MYF(MY_WME))))
    die("Could not allocate con_buf");
  p= con_buf;

2220
  if (*p != '(')
2221
    die("Syntax error in connect - expected '(' found '%c'", *p);
unknown's avatar
unknown committed
2222
  p++;
unknown's avatar
unknown committed
2223 2224 2225 2226 2227
  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
2228 2229

  /* Port */
unknown's avatar
unknown committed
2230
  p= safe_get_param(p, &con_port_str, 0);
unknown's avatar
unknown committed
2231
  if (*con_port_str)
unknown's avatar
unknown committed
2232
  {
2233 2234
    if (*con_port_str == '$')
    {
2235
      if (!(var_port= var_get(con_port_str, 0, 0, 0)))
unknown's avatar
unknown committed
2236
        die("Unknown variable '%s'", con_port_str+1);
2237
      con_port= var_port->int_val;
2238 2239
    }
    else
unknown's avatar
unknown committed
2240
    {
2241
      con_port= atoi(con_port_str);
unknown's avatar
unknown committed
2242 2243 2244 2245 2246 2247 2248 2249 2250 2251
      if (con_port == 0)
        die("Illegal argument for port: '%s'", con_port_str);
    }
  }
  else
  {
    con_port= port;
  }

  /* Sock */
unknown's avatar
unknown committed
2252
  p= safe_get_param(p, &con_sock, 0);
unknown's avatar
unknown committed
2253 2254
  if (*con_sock)
  {
2255 2256
    if (*con_sock == '$')
    {
2257
      if (!(var_sock= var_get(con_sock, 0, 0, 0)))
unknown's avatar
unknown committed
2258
        die("Unknown variable '%s'", con_sock+1);
2259
      if (!(con_sock= (char*)my_malloc(var_sock->str_val_len+1, MYF(0))))
unknown's avatar
unknown committed
2260
        die("Out of memory");
2261
      free_con_sock= 1;
2262
      memcpy(con_sock, var_sock->str_val, var_sock->str_val_len);
2263
      con_sock[var_sock->str_val_len]= 0;
2264
    }
unknown's avatar
unknown committed
2265
  }
unknown's avatar
unknown committed
2266 2267 2268 2269 2270 2271
  else
  {
    con_sock= (char*) unix_sock;
  }

  /* Options */
unknown's avatar
unknown committed
2272
  p= safe_get_param(p, &con_options, 0);
unknown's avatar
unknown committed
2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286
  while (*con_options)
  {
    char* str= con_options;
    while (*str && !my_isspace(charset_info, *str))
      str++;
    *str++= 0;
    if (!strcmp(con_options, "SSL"))
      con_ssl= 1;
    else if (!strcmp(con_options, "COMPRESS"))
      con_compress= 1;
    else
      die("Illegal option to connect: %s", con_options);
    con_options= str;
  }
2287 2288
  /* Note: 'p' is pointing into the copy 'con_buf' */
  q->last_argument= q->first_argument + (p - con_buf);
unknown's avatar
unknown committed
2289

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

unknown's avatar
unknown committed
2293 2294 2295
  if (find_connection_by_name(con_name))
    die("Connection %s already exists", con_name);

2296
  if (!mysql_init(&next_con->mysql))
unknown's avatar
unknown committed
2297
    die("Failed on mysql_init()");
unknown's avatar
unknown committed
2298
  if (opt_compress || con_compress)
2299
    mysql_options(&next_con->mysql, MYSQL_OPT_COMPRESS, NullS);
2300
  mysql_options(&next_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
unknown's avatar
unknown committed
2301
  mysql_options(&next_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name);
2302

unknown's avatar
unknown committed
2303
#ifdef HAVE_OPENSSL
unknown's avatar
unknown committed
2304
  if (opt_use_ssl || con_ssl)
unknown's avatar
unknown committed
2305 2306 2307
    mysql_ssl_set(&next_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
		  opt_ssl_capath, opt_ssl_cipher);
#endif
2308
  if (con_sock && !free_con_sock && *con_sock && *con_sock != FN_LIBCHAR)
2309
    con_sock=fn_format(buff, con_sock, TMPDIR, "",0);
unknown's avatar
unknown committed
2310
  if (!con_db[0])
2311
    con_db= db;
2312
  /* Special database to allow one to connect without a database name */
unknown's avatar
unknown committed
2313
  if (con_db && !strcmp(con_db,"*NO-ONE*"))
2314
    con_db= 0;
2315 2316
  if (q->abort_on_error)
  {
2317 2318 2319 2320
    if (safe_connect(&next_con->mysql, con_host, con_user, con_pass,
		     con_db, con_port, con_sock ? con_sock: 0))
      die("Could not open connection '%s': %d %s", con_name,
          mysql_errno(&next_con->mysql), mysql_error(&next_con->mysql));
2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332
  }
  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++;
  }
2333 2334
  if (free_con_sock)
    my_free(con_sock, MYF(MY_WME));
unknown's avatar
unknown committed
2335
  my_free(con_buf, MYF(MY_WME));
2336
  DBUG_RETURN(error);
unknown's avatar
unknown committed
2337 2338
}

2339

2340
int do_done(struct st_query *q)
2341
{
unknown's avatar
unknown committed
2342
  /* Check if empty block stack */
2343
  if (cur_block == block_stack)
2344 2345 2346
  {
    if (*q->query != '}')
      die("Stray 'end' command - end of block before beginning");
2347
    die("Stray '}' - end of block before beginning");
2348
  }
unknown's avatar
unknown committed
2349 2350 2351

  /* Test if inner block has been executed */
  if (cur_block->ok && cur_block->cmd == cmd_while)
2352
  {
unknown's avatar
unknown committed
2353 2354 2355
    /* Pop block from stack, re-execute outer block */
    cur_block--;
    parser.current_line = cur_block->line;
2356
  }
2357
  else
unknown's avatar
unknown committed
2358
  {
unknown's avatar
unknown committed
2359 2360 2361
    /* Pop block from stack, goto next line */
    cur_block--;
    parser.current_line++;
unknown's avatar
unknown committed
2362
  }
2363 2364 2365
  return 0;
}

unknown's avatar
unknown committed
2366 2367

int do_block(enum block_cmd cmd, struct st_query* q)
2368
{
2369 2370
  char *p= q->first_argument;
  const char *expr_start, *expr_end;
2371
  VAR v;
2372
  const char *cmd_name= (cmd == cmd_while ? "while" : "if");
unknown's avatar
unknown committed
2373 2374

  /* Check stack overflow */
2375
  if (cur_block == block_stack_end)
2376
    die("Nesting too deeply");
unknown's avatar
unknown committed
2377 2378 2379 2380 2381 2382

  /* 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)
2383
  {
unknown's avatar
unknown committed
2384 2385 2386 2387
    /* Inner block should be ignored too */
    cur_block++;
    cur_block->cmd= cmd;
    cur_block->ok= FALSE;
2388 2389
    return 0;
  }
unknown's avatar
unknown committed
2390

unknown's avatar
unknown committed
2391
  /* Parse and evaluate test expression */
2392
  expr_start= strchr(p, '(');
2393
  if (!expr_start)
2394 2395
    die("missing '(' in %s", cmd_name);
  expr_end= strrchr(expr_start, ')');
2396
  if (!expr_end)
2397
    die("missing ')' in %s", cmd_name);
2398 2399 2400 2401
  p= (char*)expr_end+1;

  while (*p && my_isspace(charset_info, *p))
    p++;
2402 2403
  if (*p == '{')
    die("Missing newline between %s and '{'", cmd_name);
2404
  if (*p)
2405
    die("Missing '{' after %s. Found \"%s\"", cmd_name, p);
2406

unknown's avatar
unknown committed
2407
  var_init(&v,0,0,0,0);
2408
  eval_expr(&v, ++expr_start, &expr_end);
unknown's avatar
unknown committed
2409 2410 2411 2412 2413 2414

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

unknown's avatar
unknown committed
2415
  var_free(&v);
2416
  return 0;
2417 2418
}

unknown's avatar
unknown committed
2419

2420 2421 2422 2423 2424 2425 2426 2427 2428 2429
/*
  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.
*/

2430
int my_getc(FILE *file)
2431 2432 2433
{
  if (line_buffer_pos == line_buffer)
    return fgetc(file);
2434
  return *--line_buffer_pos;
2435 2436 2437 2438
}

void my_ungetc(int c)
{
2439
  *line_buffer_pos++= (char) c;
2440 2441 2442 2443
}


my_bool end_of_query(int c)
2444
{
2445
  uint i;
2446
  char tmp[MAX_DELIMITER];
2447

2448 2449 2450 2451
  if (c != *delimiter)
    return 0;

  for (i= 1; i < delimiter_length &&
2452
	 (c= my_getc(cur_file->file)) == *(delimiter + i);
2453
       i++)
2454 2455 2456
    tmp[i]= c;

  if (i == delimiter_length)
2457 2458 2459
    return 1;					/* Found delimiter */

  /* didn't find delimiter, push back things that we read */
2460 2461 2462
  my_ungetc(c);
  while (i > 1)
    my_ungetc(tmp[--i]);
2463 2464 2465 2466
  return 0;
}


2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490
/*
  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.

*/

2491
int read_line(char *buf, int size)
unknown's avatar
unknown committed
2492 2493
{
  int c;
2494
  char quote;
2495
  char *p= buf, *buf_end= buf + size - 1;
2496
  int no_save= 0;
2497 2498
  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
2499
  DBUG_ENTER("read_line");
2500
  LINT_INIT(quote);
2501

2502
  start_lineno= cur_file->lineno;
2503 2504
  for (; p < buf_end ;)
  {
2505
    no_save= 0;
2506 2507
    c= my_getc(cur_file->file);
    if (feof(cur_file->file))
unknown's avatar
unknown committed
2508
    {
unknown's avatar
unknown committed
2509
  found_eof:
unknown's avatar
unknown committed
2510 2511
      if (cur_file->file != stdin)
      {
2512
	my_fclose(cur_file->file, MYF(0));
2513 2514
        cur_file->file= 0;
      }
2515 2516
      my_free((gptr)cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR));
      cur_file->file_name= 0;
2517
      if (cur_file == file_stack)
2518
      {
2519 2520 2521 2522 2523
        /* We're back at the first file, check if
           all { have matching }
         */
        if (cur_block != block_stack)
          die("Missing end of block");
2524

2525
        DBUG_PRINT("info", ("end of file"));
unknown's avatar
unknown committed
2526
	DBUG_RETURN(1);
2527
      }
2528
      cur_file--;
2529
      start_lineno= cur_file->lineno;
unknown's avatar
unknown committed
2530
      continue;
2531
    }
2532

2533 2534
    /* Line counting is independent of state */
    if (c == '\n')
2535
      cur_file->lineno++;
2536

2537 2538
    switch(state) {
    case R_NORMAL:
2539
      /*  Only accept '{' in the beginning of a line */
2540 2541 2542
      if (end_of_query(c))
      {
	*p= 0;
unknown's avatar
unknown committed
2543
	DBUG_RETURN(0);
2544
      }
2545 2546 2547 2548 2549
      else if (c == '\'' || c == '"' || c == '`')
      {
        quote= c;
	state= R_Q;
      }
2550
      else if (c == '\n')
2551
      {
2552
	state = R_LINE_START;
2553
      }
2554 2555 2556 2557
      break;
    case R_COMMENT:
      if (c == '\n')
      {
2558
	*p= 0;
unknown's avatar
unknown committed
2559
	DBUG_RETURN(0);
2560 2561 2562
      }
      break;
    case R_LINE_START:
2563
      /* Only accept start of comment if this is the first line in query */
2564 2565
      if ((cur_file->lineno == start_lineno) &&
	  (c == '#' || c == '-' || parsing_disabled))
2566 2567 2568
      {
	state = R_COMMENT;
      }
2569
      else if (my_isspace(charset_info, c))
2570 2571
      {
	if (c == '\n')
2572
	  start_lineno= cur_file->lineno; /* Query hasn't started yet */
2573
	no_save= 1;
2574
      }
2575 2576
      else if (c == '}')
      {
2577 2578
	*buf++= '}';
	*buf= 0;
unknown's avatar
unknown committed
2579
	DBUG_RETURN(0);
2580
      }
2581
      else if (end_of_query(c) || c == '{')
2582
      {
2583
	*p= 0;
unknown's avatar
unknown committed
2584
	DBUG_RETURN(0);
2585
      }
2586
      else if (c == '\'' || c == '"' || c == '`')
2587
      {
2588 2589
        quote= c;
	state= R_Q;
2590
      }
2591
      else
2592
	state= R_NORMAL;
2593
      break;
unknown's avatar
unknown committed
2594

2595 2596 2597
    case R_Q:
      if (c == quote)
	state= R_Q_IN_Q;
2598
      else if (c == '\\')
2599
	state= R_SLASH_IN_Q;
2600
      break;
2601
    case R_Q_IN_Q:
2602 2603 2604
      if (end_of_query(c))
      {
	*p= 0;
unknown's avatar
unknown committed
2605
	DBUG_RETURN(0);
2606
      }
2607
      if (c != quote)
2608
	state= R_NORMAL;
2609
      else
2610
	state= R_Q;
2611
      break;
2612 2613
    case R_SLASH_IN_Q:
      state= R_Q;
2614
      break;
2615

unknown's avatar
unknown committed
2616
    }
2617 2618

    if (!no_save)
unknown's avatar
unknown committed
2619 2620 2621 2622 2623
    {
      /* 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
2624 2625
      /* We give up if multibyte character is started but not */
      /* completed before we pass buf_end */
unknown's avatar
unknown committed
2626 2627 2628 2629 2630 2631 2632 2633 2634
      if ((charlen > 1) && (p + charlen) <= buf_end)
      {
	int i;
	char* mb_start = p;

	*p++ = c;

	for (i= 1; i < charlen; i++)
	{
2635
	  if (feof(cur_file->file))
unknown's avatar
unknown committed
2636
	    goto found_eof;	/* FIXME: could we just break here?! */
2637
	  c= my_getc(cur_file->file);
unknown's avatar
unknown committed
2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651
	  *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;
    }
2652
  }
2653
  *p= 0;					/* Always end with \0 */
2654
  DBUG_RETURN(feof(cur_file->file));
unknown's avatar
unknown committed
2655 2656
}

2657 2658 2659 2660
/*
  Create a query from a set of lines

  SYNOPSIS
2661
    read_query()
2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674
    q_ptr pointer where to return the new query

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


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

*/
unknown's avatar
unknown committed
2675

2676 2677
static char read_query_buf[MAX_QUERY];

2678
int read_query(struct st_query** q_ptr)
unknown's avatar
unknown committed
2679
{
2680
  char *p= read_query_buf;
2681
  struct st_query* q;
unknown's avatar
unknown committed
2682
  DBUG_ENTER("read_query");
2683

2684 2685
  if (parser.current_line < parser.read_lines)
  {
2686
    get_dynamic(&q_lines, (gptr) q_ptr, parser.current_line) ;
unknown's avatar
unknown committed
2687
    DBUG_RETURN(0);
2688
  }
2689
  if (!(*q_ptr= q= (struct st_query*) my_malloc(sizeof(*q), MYF(MY_WME))) ||
unknown's avatar
unknown committed
2690 2691
      insert_dynamic(&q_lines, (gptr) &q))
    die(NullS);
2692

2693 2694 2695
  q->record_file[0]= 0;
  q->require_file= 0;
  q->first_word_len= 0;
unknown's avatar
unknown committed
2696

2697
  q->type= Q_UNKNOWN;
2698
  q->query_buf= q->query= 0;
2699
  read_query_buf[0]= 0;
2700
  if (read_line(read_query_buf, sizeof(read_query_buf)))
unknown's avatar
unknown committed
2701
  {
2702
    check_eol_junk(read_query_buf);
unknown's avatar
unknown committed
2703
    DBUG_RETURN(1);
unknown's avatar
unknown committed
2704
  }
2705

2706
  DBUG_PRINT("info", ("query: %s", read_query_buf));
2707 2708
  if (*p == '#')
  {
2709
    q->type= Q_COMMENT;
unknown's avatar
unknown committed
2710 2711
    /* This goto is to avoid losing the "expected error" info. */
    goto end;
2712
  }
2713 2714 2715 2716 2717 2718 2719 2720
  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
2721
  if (p[0] == '-' && p[1] == '-')
2722
  {
2723 2724
    q->type= Q_COMMENT_WITH_COMMAND;
    p+= 2;					/* To calculate first word */
2725
  }
2726
  else if (!parsing_disabled)
2727
  {
2728
    while (*p && my_isspace(charset_info, *p))
unknown's avatar
unknown committed
2729
      p++ ;
2730
  }
unknown's avatar
unknown committed
2731 2732

end:
2733
  while (*p && my_isspace(charset_info, *p))
unknown's avatar
unknown committed
2734
    p++;
unknown's avatar
unknown committed
2735

2736
  if (!(q->query_buf= q->query= my_strdup(p, MYF(MY_WME))))
2737 2738 2739
    die(NullS);

  /* Calculate first word and first argument */
2740 2741 2742
  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
2743
    p++;
2744 2745
  q->first_argument= p;
  q->end= strend(q->query);
2746
  parser.read_lines++;
unknown's avatar
unknown committed
2747
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2748 2749
}

2750 2751 2752

static struct my_option my_long_options[] =
{
unknown's avatar
unknown committed
2753 2754
  {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
   0, 0, 0, 0, 0, 0},
2755
  {"basedir", 'b', "Basedir for tests.", (gptr*) &opt_basedir,
2756
   (gptr*) &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2757
  {"big-test", 'B', "Define BIG_TEST to 1.", (gptr*) &opt_big_test,
2758
   (gptr*) &opt_big_test, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
2759
  {"compress", 'C', "Use the compressed server/client protocol.",
2760 2761
   (gptr*) &opt_compress, (gptr*) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
   0, 0, 0},
unknown's avatar
unknown committed
2762 2763 2764 2765 2766 2767 2768 2769 2770
  {"database", 'D', "Database to use.", (gptr*) &db, (gptr*) &db, 0,
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#ifdef DBUG_OFF
  {"debug", '#', "This is a non-debug version. Catch this and exit",
   0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
#else
  {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
#endif
2771 2772
  {"host", 'h', "Connect to host.", (gptr*) &host, (gptr*) &host, 0,
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2773
  {"manager-host", OPT_MANAGER_HOST, "Undocumented: Used for debugging.",
2774 2775
   (gptr*) &manager_host, (gptr*) &manager_host, 0, GET_STR, REQUIRED_ARG,
   0, 0, 0, 0, 0, 0},
2776
  {"manager-password", OPT_MANAGER_PASSWD, "Undocumented: Used for debugging.",
2777
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2778
  {"manager-port", OPT_MANAGER_PORT, "Undocumented: Used for debugging.",
2779 2780
   (gptr*) &manager_port, (gptr*) &manager_port, 0, GET_INT, REQUIRED_ARG,
   MYSQL_MANAGER_PORT, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
2781 2782 2783
  {"manager-user", OPT_MANAGER_USER, "Undocumented: Used for debugging.",
   (gptr*) &manager_user, (gptr*) &manager_user, 0, GET_STR, REQUIRED_ARG, 0,
   0, 0, 0, 0, 0},
2784
  {"manager-wait-timeout", OPT_MANAGER_WAIT_TIMEOUT,
2785
   "Undocumented: Used for debugging.", (gptr*) &manager_wait_timeout,
2786 2787 2788 2789
   (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,
2790
   (gptr*) &port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2791 2792 2793
  {"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},
2794 2795 2796 2797 2798 2799 2800 2801 2802
  {"sp-protocol", OPT_SP_PROTOCOL, "Use stored procedures for select",
   (gptr*) &sp_protocol, (gptr*) &sp_protocol, 0,
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"cursor-protocol", OPT_CURSOR_PROTOCOL, "Use cursors for prepared statment",
   (gptr*) &cursor_protocol, (gptr*) &cursor_protocol, 0,
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"view-protocol", OPT_VIEW_PROTOCOL, "Use views for select",
   (gptr*) &view_protocol, (gptr*) &view_protocol, 0,
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
2803 2804 2805 2806 2807 2808 2809
  {"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},
2810
  {"server-arg", 'A', "Send enbedded server this as a paramenter.",
2811
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2812
  {"server-file", 'F', "Read embedded server arguments from file.",
2813 2814 2815
   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},
2816
  {"skip-safemalloc", OPT_SKIP_SAFEMALLOC,
2817
   "Don't use the memory allocation checking.", 0, 0, 0, GET_NO_ARG, NO_ARG,
2818
   0, 0, 0, 0, 0, 0},
2819
  {"sleep", 'T', "Sleep always this many seconds on sleep commands.",
2820 2821 2822 2823 2824
   (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
2825
#include "sslopt-longopts.h"
2826 2827
  {"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
2828 2829
  {"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},
2830
  {"tmpdir", 't', "Temporary directory where sockets are put.",
2831 2832 2833 2834 2835 2836 2837 2838
   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
2839 2840
};

unknown's avatar
unknown committed
2841 2842 2843

#include <help_start.h>

unknown's avatar
unknown committed
2844 2845 2846 2847 2848 2849 2850 2851 2852
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();
2853
  printf("MySQL AB, by Sasha, Matt, Monty & Jani\n");
unknown's avatar
unknown committed
2854 2855 2856
  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);
2857
  my_print_help(my_long_options);
unknown's avatar
unknown committed
2858
  printf("  --no-defaults       Don't read default options from any options file.\n");
2859
  my_print_variables(my_long_options);
unknown's avatar
unknown committed
2860 2861
}

unknown's avatar
unknown committed
2862 2863
#include <help_end.h>

2864 2865 2866 2867 2868

static my_bool
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
	       char *argument)
{
2869
  switch(optid) {
2870
  case '#':
unknown's avatar
unknown committed
2871
#ifndef DBUG_OFF
2872
    DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysqltest.trace");
unknown's avatar
unknown committed
2873
#endif
2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891
    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);
2892
      DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0);
2893
      if (!(cur_file->file=
2894
            my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0))))
2895 2896
	die("Could not open %s: errno = %d", buff, errno);
      cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
2897
      cur_file->lineno= 1;
2898 2899
      break;
    }
unknown's avatar
unknown committed
2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912
  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;
    }
2913 2914 2915 2916 2917 2918
  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 */
2919
      tty_password= 0;
2920 2921 2922 2923
    }
    else
      tty_password= 1;
    break;
unknown's avatar
unknown committed
2924
#include <sslopt-case.h>
2925 2926 2927 2928 2929 2930 2931 2932 2933
  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*) "";
    }
2934 2935 2936
    if (embedded_server_arg_count == MAX_SERVER_ARGS-1 ||
        !(embedded_server_args[embedded_server_arg_count++]=
          my_strdup(argument, MYF(MY_FAE))))
2937 2938 2939 2940 2941 2942 2943 2944
    {
      die("Can't use server argument");
    }
    break;
  case 'F':
    if (read_server_arguments(argument))
      die(NullS);
    break;
2945 2946 2947 2948 2949
  case OPT_SKIP_SAFEMALLOC:
#ifdef SAFEMALLOC
    sf_malloc_quick=1;
#endif
    break;
2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960
  case 'V':
    print_version();
    exit(0);
  case '?':
    usage();
    exit(1);
  }
  return 0;
}


unknown's avatar
unknown committed
2961 2962 2963
int parse_args(int argc, char **argv)
{
  load_defaults("my",load_default_groups,&argc,&argv);
2964
  default_argv= argv;
2965

2966
  if ((handle_options(&argc, &argv, my_long_options, get_one_option)))
unknown's avatar
unknown committed
2967
    exit(1);
unknown's avatar
unknown committed
2968 2969 2970 2971 2972 2973 2974

  if (argc > 1)
  {
    usage();
    exit(1);
  }
  if (argc == 1)
2975
    db= *argv;
unknown's avatar
unknown committed
2976 2977 2978 2979 2980 2981 2982
  if (tty_password)
    pass=get_tty_password(NullS);

  return 0;
}


unknown's avatar
unknown committed
2983
/*
2984
   Write the content of str into file
unknown's avatar
unknown committed
2985

2986 2987 2988 2989 2990 2991
   SYNOPSIS
   str_to_file
   fname - name of file to truncate/create and write to
   str - content to write to file
   size - size of content witten to file
*/
unknown's avatar
unknown committed
2992

2993
static void str_to_file(const char *fname, char *str, int size)
unknown's avatar
unknown committed
2994 2995
{
  int fd;
2996 2997 2998 2999
  char buff[FN_REFLEN];
  if (!test_if_hard_path(fname))
  {
    strxmov(buff, opt_basedir, fname, NullS);
unknown's avatar
unknown committed
3000
    fname= buff;
3001 3002
  }
  fn_format(buff,fname,"","",4);
unknown's avatar
unknown committed
3003

unknown's avatar
unknown committed
3004
  if ((fd= my_open(buff, O_WRONLY | O_CREAT | O_TRUNC,
3005
		    MYF(MY_WME | MY_FFNF))) < 0)
3006
    die("Could not open %s: errno = %d", buff, errno);
3007
  if (my_write(fd, (byte*)str, size, MYF(MY_WME|MY_FNABP)))
unknown's avatar
unknown committed
3008 3009 3010 3011
    die("write failed");
  my_close(fd, MYF(0));
}

unknown's avatar
unknown committed
3012

3013
void dump_result_to_reject_file(const char *record_file, char *buf, int size)
unknown's avatar
unknown committed
3014
{
unknown's avatar
unknown committed
3015
  char reject_file[FN_REFLEN];
unknown's avatar
unknown committed
3016
  str_to_file(fn_format(reject_file, record_file,"",".reject",2), buf, size);
unknown's avatar
unknown committed
3017 3018
}

3019 3020 3021

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

unknown's avatar
unknown committed
3022 3023
static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
				      int len)
3024 3025 3026 3027 3028
{
  if (glob_replace)
  {
    len=(int) replace_strings(glob_replace, &out_buff, &out_length, val);
    if (len == -1)
3029
      die("Out of memory in replace");
3030 3031 3032 3033 3034
    val=out_buff;
  }
  dynstr_append_mem(ds, val, len);
}

unknown's avatar
unknown committed
3035

3036 3037 3038 3039 3040 3041 3042
/* 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));
}

3043

3044 3045
/*
  Append all results to the dynamic string separated with '\t'
3046
  Values may be converted with 'replace_column'
3047 3048 3049 3050 3051
*/

static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
{
  MYSQL_ROW row;
3052
  uint num_fields= mysql_num_fields(res);
3053
  MYSQL_FIELD *fields= !display_result_vertically ? 0 : mysql_fetch_fields(res);
3054
  ulong *lengths;
3055 3056
  while ((row = mysql_fetch_row(res)))
  {
3057
    uint i;
3058 3059 3060 3061 3062
    lengths = mysql_fetch_lengths(res);
    for (i = 0; i < num_fields; i++)
    {
      const char *val= row[i];
      ulonglong len= lengths[i];
3063 3064 3065 3066 3067 3068

      if (i < max_replace_column && replace_column[i])
      {
	val= replace_column[i];
	len= strlen(val);
      }
3069 3070
      if (!val)
      {
3071 3072
	val= "NULL";
	len= 4;
3073
      }
3074 3075 3076 3077
      if (!display_result_vertically)
      {
	if (i)
	  dynstr_append_mem(ds, "\t", 1);
3078
	replace_dynstr_append_mem(ds, val, (int)len);
3079 3080 3081 3082
      }
      else
      {
	dynstr_append(ds, fields[i].name);
3083
	dynstr_append_mem(ds, "\t", 1);
3084
	replace_dynstr_append_mem(ds, val, (int)len);
3085 3086
	dynstr_append_mem(ds, "\n", 1);
      }
3087
    }
3088 3089
    if (!display_result_vertically)
      dynstr_append_mem(ds, "\n", 1);
3090
  }
3091
  free_replace_column();
3092 3093
}

3094

3095
/*
unknown's avatar
unknown committed
3096
  Append all results from ps execution to the dynamic string separated
3097
  with '\t'. Values may be converted with 'replace_column'
3098
*/
3099

unknown's avatar
unknown committed
3100 3101
static void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt,
			       MYSQL_FIELD *field, uint num_fields)
3102
{
3103 3104 3105 3106 3107
  MYSQL_BIND *bind;
  my_bool *is_null;
  ulong *length;
  ulonglong num_rows;
  uint col_idx, row_idx;
3108

3109 3110 3111 3112 3113 3114 3115
  /* Allocate array with bind structs, lengths and NULL flags */
  bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND),
				MYF(MY_WME | MY_FAE | MY_ZEROFILL));
  length= (ulong*) my_malloc(num_fields * sizeof(ulong),
			     MYF(MY_WME | MY_FAE));
  is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool),
				MYF(MY_WME | MY_FAE));
3116

3117
  for (col_idx= 0; col_idx < num_fields; col_idx++)
unknown's avatar
unknown committed
3118
  {
3119
    /* Allocate data for output */
unknown's avatar
unknown committed
3120
    uint max_length= field[col_idx].max_length + 1;
3121
    char *str_data= (char *) my_malloc(max_length, MYF(MY_WME | MY_FAE));
unknown's avatar
unknown committed
3122

3123 3124 3125 3126 3127
    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];
unknown's avatar
unknown committed
3128

unknown's avatar
unknown committed
3129 3130 3131 3132
    DBUG_PRINT("bind", ("col[%d]: buffer_type: %d, buffer_length: %d",
			col_idx,
			bind[col_idx].buffer_type,
			bind[col_idx].buffer_length));
3133
  }
unknown's avatar
unknown committed
3134

3135 3136
  /* Fill in the data into the structures created above */
  if (mysql_stmt_bind_result(stmt, bind))
unknown's avatar
unknown committed
3137
    die("mysql_stmt_bind_result failed: %d: %s",
3138
	mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
3139

3140 3141 3142 3143 3144 3145 3146
  /* Read result from each row */
  num_rows= mysql_stmt_num_rows(stmt);
  for (row_idx= 0; row_idx < num_rows; row_idx++)
  {
    if (mysql_stmt_fetch(stmt))
      die("mysql_stmt_fetch failed: %d %s",
	  mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
3147

3148 3149
    /* Read result from each column */
    for (col_idx= 0; col_idx < num_fields; col_idx++)
3150
    {
unknown's avatar
unknown committed
3151 3152
      const char *val;
      ulonglong len;
3153
      if (col_idx < max_replace_column && replace_column[col_idx])
3154
      {
3155 3156
	val= replace_column[col_idx];
	len= strlen(val);
3157
      }
unknown's avatar
unknown committed
3158
      else if (*bind[col_idx].is_null)
3159
      {
3160 3161
	val= "NULL";
	len= 4;
3162
      }
unknown's avatar
unknown committed
3163
      else
3164
      {
unknown's avatar
unknown committed
3165 3166 3167
	val= (const char *) bind[col_idx].buffer;
	len= *bind[col_idx].length;
      }
3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179
      if (!display_result_vertically)
      {
	if (col_idx)                      /* No tab before first col */
	  dynstr_append_mem(ds, "\t", 1);
	replace_dynstr_append_mem(ds, val, (int)len);
      }
      else
      {
	dynstr_append(ds, field[col_idx].name);
	dynstr_append_mem(ds, "\t", 1);
	replace_dynstr_append_mem(ds, val, (int)len);
	dynstr_append_mem(ds, "\n", 1);
3180
      }
3181
    }
3182 3183
    if (!display_result_vertically)
      dynstr_append_mem(ds, "\n", 1);
3184 3185
  }

3186 3187 3188
  if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
    die("fetch didn't end with MYSQL_NO_DATA from statement: %d %s",
	mysql_stmt_error(stmt), mysql_stmt_errno(stmt));
3189

3190
  free_replace_column();
3191

3192 3193 3194 3195 3196 3197 3198 3199 3200
  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));
unknown's avatar
unknown committed
3201 3202 3203
}


3204
/*
3205
  Append metadata for fields to output
3206 3207
*/

3208
static void append_metadata(DYNAMIC_STRING *ds,
unknown's avatar
unknown committed
3209
			    MYSQL_FIELD *field,
3210
			    uint num_fields)
3211
{
3212 3213 3214 3215
  MYSQL_FIELD *field_end;
  dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t"
                "Column_alias\tType\tLength\tMax length\tIs_null\t"
                "Flags\tDecimals\tCharsetnr\n");
3216

3217 3218 3219
  for (field_end= field+num_fields ;
       field < field_end ;
       field++)
3220
  {
3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249
    char buff[22];
    dynstr_append_mem(ds, field->catalog,
                          field->catalog_length);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, field->db, field->db_length);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, field->org_table,
                          field->org_table_length);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, field->table,
                          field->table_length);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, field->org_name,
                          field->org_name_length);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, field->name, field->name_length);
    dynstr_append_mem(ds, "\t", 1);
    int10_to_str((int) field->type, buff, 10);
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\t", 1);
    longlong10_to_str((unsigned int) field->length, buff, 10);
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\t", 1);
    longlong10_to_str((unsigned int) field->max_length, buff, 10);
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\t", 1);
    dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ?
                                   "N" : "Y"), 1);
    dynstr_append_mem(ds, "\t", 1);
3250

3251 3252 3253 3254 3255 3256 3257 3258 3259
    int10_to_str((int) field->flags, buff, 10);
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\t", 1);
    int10_to_str((int) field->decimals, buff, 10);
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\t", 1);
    int10_to_str((int) field->charsetnr, buff, 10);
    dynstr_append(ds, buff);
    dynstr_append_mem(ds, "\n", 1);
unknown's avatar
unknown committed
3260
  }
3261 3262
}

unknown's avatar
unknown committed
3263

3264 3265 3266
/*
  Append affected row count and other info to output
*/
3267

unknown's avatar
unknown committed
3268
static void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows,
unknown's avatar
unknown committed
3269
			const char *info)
unknown's avatar
unknown committed
3270
{
3271
  char buf[40];
unknown's avatar
unknown committed
3272
  sprintf(buf,"affected rows: %llu\n", affected_rows);
3273 3274
  dynstr_append(ds, buf);
  if (info)
unknown's avatar
unknown committed
3275
  {
3276 3277 3278
    dynstr_append(ds, "info: ");
    dynstr_append(ds, info);
    dynstr_append_mem(ds, "\n", 1);
unknown's avatar
unknown committed
3279
  }
3280
}
unknown's avatar
unknown committed
3281 3282


unknown's avatar
unknown committed
3283 3284
/*
   Display the table headings with the names tab separated
3285
*/
unknown's avatar
unknown committed
3286 3287 3288

static void append_table_headings(DYNAMIC_STRING *ds,
				  MYSQL_FIELD *field,
3289 3290 3291 3292
				  uint num_fields)
{
  uint col_idx;
  for (col_idx= 0; col_idx < num_fields; col_idx++)
3293
  {
3294 3295 3296
    if (col_idx)
      dynstr_append_mem(ds, "\t", 1);
    replace_dynstr_append(ds, field[col_idx].name);
3297
  }
3298 3299 3300 3301
  dynstr_append_mem(ds, "\n", 1);
}

/*
unknown's avatar
unknown committed
3302 3303 3304 3305
  Fetch warnings from server and append to ds

  RETURN VALUE
   Number of warnings appended to ds
3306 3307
*/

unknown's avatar
unknown committed
3308
static int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql)
3309 3310
{
  uint count;
3311
  MYSQL_RES *warn_res;
3312 3313 3314
  DBUG_ENTER("append_warnings");

  if (!(count= mysql_warning_count(mysql)))
unknown's avatar
unknown committed
3315
    DBUG_RETURN(0);
3316 3317 3318 3319 3320 3321 3322

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

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

3327
  if (!(warn_res= mysql_store_result(mysql)))
3328 3329
    die("Warning count is %u but didn't get any warnings",
	count);
unknown's avatar
unknown committed
3330

3331 3332 3333
  append_result(ds, warn_res);
  mysql_free_result(warn_res);

3334 3335
  DBUG_PRINT("warnings", ("%s", ds->str));

unknown's avatar
unknown committed
3336
  DBUG_RETURN(count);
3337 3338 3339
}


unknown's avatar
unknown committed
3340

3341 3342
/*
  Run query using MySQL C API
unknown's avatar
unknown committed
3343

3344 3345 3346 3347
  SYNPOSIS
  run_query_normal
  mysql - mysql handle
  command - currrent command pointer
3348
  flags -flags indicating wheter to SEND and/or REAP
3349 3350 3351 3352 3353 3354 3355 3356
  query - query string to execute
  query_len - length query string to execute
  ds - output buffer wherte to store result form query

  RETURN VALUE
  error - function will not return
*/

unknown's avatar
unknown committed
3357 3358
static void run_query_normal(MYSQL *mysql, struct st_query *command,
			     int flags, char *query, int query_len,
3359
			     DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings)
3360 3361 3362 3363 3364 3365
{
  MYSQL_RES *res= 0;
  int err= 0, counter= 0;
  DBUG_ENTER("run_query_normal");
  DBUG_PRINT("enter",("flags: %d", flags));
  DBUG_PRINT("enter", ("query: '%-.60s'", query));
unknown's avatar
unknown committed
3366

3367 3368
  if (flags & QUERY_SEND)
  {
unknown's avatar
unknown committed
3369
    /*
3370
       Send the query
3371 3372 3373 3374 3375 3376 3377
     */
    if (mysql_send_query(mysql, query, query_len))
    {
      handle_error(query, command, mysql_errno(mysql), mysql_error(mysql),
		   mysql_sqlstate(mysql), ds);
      goto end;
    }
3378
  }
unknown's avatar
unknown committed
3379

3380 3381
  if (!(flags & QUERY_REAP))
    DBUG_VOID_RETURN;
unknown's avatar
unknown committed
3382

3383
  do
3384
  {
3385
    /*
unknown's avatar
unknown committed
3386
      When  on first result set, call mysql_read_query_result to retrieve
3387 3388 3389
      answer to the query sent earlier
     */
    if ((counter==0) && mysql_read_query_result(mysql))
3390
    {
3391 3392 3393 3394
      handle_error(query, command, mysql_errno(mysql), mysql_error(mysql),
		   mysql_sqlstate(mysql), ds);
      goto end;

3395
    }
3396

unknown's avatar
unknown committed
3397 3398
    /*
       Store the result. If res is NULL, use mysql_field_count to
3399 3400 3401
       determine if that was expected
     */
    if (!(res= mysql_store_result(mysql)) && mysql_field_count(mysql))
unknown's avatar
unknown committed
3402
    {
3403 3404
      handle_error(query, command, mysql_errno(mysql), mysql_error(mysql),
		   mysql_sqlstate(mysql), ds);
3405 3406
      goto end;
    }
3407

3408
    if (!disable_result_log)
3409
    {
unknown's avatar
unknown committed
3410
      ulonglong affected_rows;    /* Ok to be undef if 'disable_info' is set */
unknown's avatar
unknown committed
3411
      LINT_INIT(affected_rows);
unknown's avatar
unknown committed
3412

3413
      if (res)
3414
      {
3415
	MYSQL_FIELD *fields= mysql_fetch_fields(res);
3416
	uint num_fields= mysql_num_fields(res);
3417

3418
	if (display_metadata)
3419
          append_metadata(ds, fields, num_fields);
3420

3421
	if (!display_result_vertically)
3422 3423
	  append_table_headings(ds, fields, num_fields);

3424
	append_result(ds, res);
3425
      }
unknown's avatar
unknown committed
3426

unknown's avatar
unknown committed
3427
      /*
3428
        Need to call mysql_affected_rows() before the "new"
unknown's avatar
unknown committed
3429 3430 3431
        query to find the warnings
      */
      if (!disable_info)
unknown's avatar
unknown committed
3432
        affected_rows= mysql_affected_rows(mysql);
unknown's avatar
unknown committed
3433

3434 3435 3436 3437 3438
      /*
        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.
      */
3439
      if (!disable_warnings && !mysql_more_results(mysql))
3440
      {
3441
	if (append_warnings(ds_warnings, mysql) || ds_warnings->length)
3442 3443
	{
	  dynstr_append_mem(ds, "Warnings:\n", 10);
3444
	  dynstr_append_mem(ds, ds_warnings->str, ds_warnings->length);
3445
	}
3446
      }
3447

unknown's avatar
unknown committed
3448
      if (!disable_info)
3449
	append_info(ds, affected_rows, mysql_info(mysql));
3450
    }
3451

3452 3453 3454 3455
    if (res)
      mysql_free_result(res);
    counter++;
  } while (!(err= mysql_next_result(mysql)));
3456 3457
  if (err > 0)
  {
3458 3459 3460
    /* We got an error from mysql_next_result, maybe expected */
    handle_error(query, command, mysql_errno(mysql), mysql_error(mysql),
		 mysql_sqlstate(mysql), ds);
3461 3462
    goto end;
  }
3463
  DBUG_ASSERT(err == -1); /* Successful and there are no more results */
3464

3465
  /* If we come here the query is both executed and read successfully */
3466
  handle_no_error(command);
3467

3468
end:
3469
  free_replace();
3470 3471 3472 3473 3474 3475

  /*
    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.
  */
3476
  var_set_errno(mysql_errno(mysql));
3477
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
3478 3479 3480
}


3481
/*
3482
  Handle errors which occurred during execution
3483 3484

  SYNOPSIS
3485
    handle_error()
3486 3487
      query - query string
      q     - query context
3488 3489 3490
      err_errno - error number
      err_error - error message
      err_sqlstate - sql state
3491 3492 3493 3494 3495 3496 3497
      ds    - dynamic string which is used for output buffer

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

  RETURN VALUE
3498
    error - function will not return
3499 3500
*/

3501 3502 3503
static void handle_error(const char *query, struct st_query *q,
			 unsigned int err_errno, const char *err_error,
			 const char *err_sqlstate, DYNAMIC_STRING *ds)
3504 3505
{
  uint i;
unknown's avatar
unknown committed
3506

3507
  DBUG_ENTER("handle_error");
3508 3509 3510

  if (q->require_file)
    abort_not_supported_test();
unknown's avatar
unknown committed
3511

3512
  if (q->abort_on_error)
3513
    die("query '%s' failed: %d: %s", query, err_errno, err_error);
3514 3515

  for (i= 0 ; (uint) i < q->expected_errors ; i++)
3516
  {
3517 3518 3519 3520
    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)))
3521
    {
3522
      if (!disable_result_log)
3523
      {
3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537
        if (q->expected_errors == 1)
        {
          /* Only log error if there is one possible error */
          dynstr_append_mem(ds, "ERROR ", 6);
          replace_dynstr_append(ds, err_sqlstate);
          dynstr_append_mem(ds, ": ", 2);
          replace_dynstr_append(ds, err_error);
          dynstr_append_mem(ds,"\n",1);
        }
        /* Don't log error if we may not get an error */
        else if (q->expected_errno[0].type == ERR_SQLSTATE ||
                 (q->expected_errno[0].type == ERR_ERRNO &&
                  q->expected_errno[0].code.errnum != 0))
          dynstr_append(ds,"Got one of the listed errors\n");
3538
      }
3539
      /* OK */
3540
      DBUG_VOID_RETURN;
3541
    }
3542
  }
3543

3544
  DBUG_PRINT("info",("i: %d  expected_errors: %d", i, q->expected_errors));
3545

3546 3547 3548 3549 3550 3551 3552 3553
  if (!disable_result_log)
  {
    dynstr_append_mem(ds, "ERROR ",6);
    replace_dynstr_append(ds, err_sqlstate);
    dynstr_append_mem(ds, ": ", 2);
    replace_dynstr_append(ds, err_error);
    dynstr_append_mem(ds, "\n", 1);
  }
3554

3555 3556 3557
  if (i)
  {
    if (q->expected_errno[0].type == ERR_ERRNO)
3558 3559
      die("query '%s' failed with wrong errno %d: '%s', instead of %d...",
          q->query, err_errno, err_error, q->expected_errno[0].code.errnum);
3560
    else
3561 3562 3563
      die("query '%s' failed with wrong sqlstate %s: '%s', instead of %s...",
          q->query, err_sqlstate, err_error,
	  q->expected_errno[0].code.sqlstate);
3564
  }
3565

3566
  DBUG_VOID_RETURN;
3567 3568 3569 3570
}


/*
3571
  Handle absence of errors after execution
3572 3573

  SYNOPSIS
3574
    handle_no_error()
3575 3576 3577
      q - context of query

  RETURN VALUE
3578
    error - function will not return
3579 3580
*/

3581
static void handle_no_error(struct st_query *q)
3582
{
3583
  DBUG_ENTER("handle_no_error");
3584 3585 3586 3587 3588

  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 */
3589 3590
    die("query '%s' succeeded - should have failed with errno %d...",
        q->query, q->expected_errno[0].code.errnum);
3591 3592 3593 3594 3595
  }
  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 */
3596 3597
    die("query '%s' succeeded - should have failed with sqlstate %s...",
        q->query, q->expected_errno[0].code.sqlstate);
3598 3599
  }

3600
  DBUG_VOID_RETURN;
3601 3602
}

3603 3604

/*
3605
  Run query using prepared statement C API
unknown's avatar
unknown committed
3606

3607 3608 3609 3610 3611 3612 3613
  SYNPOSIS
  run_query_stmt
  mysql - mysql handle
  command - currrent command pointer
  query - query string to execute
  query_len - length query string to execute
  ds - output buffer wherte to store result form query
3614

3615 3616
  RETURN VALUE
  error - function will not return
3617 3618
*/

unknown's avatar
unknown committed
3619
static void run_query_stmt(MYSQL *mysql, struct st_query *command,
3620 3621
			   char *query, int query_len, DYNAMIC_STRING *ds,
			   DYNAMIC_STRING *ds_warnings)
3622 3623 3624
{
  MYSQL_RES *res= NULL;     /* Note that here 'res' is meta data result set */
  MYSQL_STMT *stmt;
3625
  DYNAMIC_STRING ds_prepare_warnings;
unknown's avatar
unknown committed
3626
  DYNAMIC_STRING ds_execute_warnings;
3627
  DBUG_ENTER("run_query_stmt");
3628
  DBUG_PRINT("query", ("'%-.60s'", query));
3629 3630

  /*
3631
    Init a new stmt if it's not alreday one created for this connectoon
3632
  */
3633
  if(!(stmt= cur_con->stmt))
3634
  {
3635 3636 3637
    if (!(stmt= mysql_stmt_init(mysql)))
      die("unable to init stmt structure");
    cur_con->stmt= stmt;
3638 3639
  }

unknown's avatar
unknown committed
3640 3641
  /* Init dynamic strings for warnings */
  if (!disable_warnings)
3642
  {
unknown's avatar
unknown committed
3643 3644
    init_dynamic_string(&ds_prepare_warnings, NULL, 0, 256);
    init_dynamic_string(&ds_execute_warnings, NULL, 0, 256);
3645 3646 3647
  }

  /*
3648
    Prepare the query
3649
  */
3650
  if (mysql_stmt_prepare(stmt, query, query_len))
3651
  {
3652 3653
    handle_error(query, command,  mysql_stmt_errno(stmt),
		 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
3654
    goto end;
3655 3656 3657
  }

  /*
unknown's avatar
unknown committed
3658 3659
    Get the warnings from mysql_stmt_prepare and keep them in a
    separate string
3660
  */
3661 3662
  if (!disable_warnings)
    append_warnings(&ds_prepare_warnings, mysql);
3663 3664

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

3669 3670 3671
#ifdef BUG14013_FIXED
  /*
    Use cursor when retrieving result
3672
  */
3673 3674
  if (cursor_protocol_enabled)
  {
unknown's avatar
unknown committed
3675
    ulong type= CURSOR_TYPE_READ_ONLY;
3676
    if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type))
unknown's avatar
unknown committed
3677
      die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s",
3678 3679 3680
	  mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
  }
#endif
3681

3682 3683 3684
  /*
    Execute the query
   */
unknown's avatar
unknown committed
3685
  if (mysql_stmt_execute(stmt))
3686
  {
3687 3688
    handle_error(query, command, mysql_stmt_errno(stmt),
		 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
3689
    goto end;
3690 3691 3692 3693 3694 3695 3696 3697 3698
  }

  /*
    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;
3699
    if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one))
unknown's avatar
unknown committed
3700
      die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s",
3701
	  mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
3702 3703 3704 3705 3706 3707
  }

  /*
    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!
  */
3708
  if (mysql_stmt_store_result(stmt))
3709
  {
3710 3711
    handle_error(query, command, mysql_stmt_errno(stmt),
		 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
3712
    goto end;
3713
  }
3714

3715
  /* If we got here the statement was both executed and read succeesfully */
3716 3717
  handle_no_error(command);
  if (!disable_result_log)
3718
  {
3719 3720 3721 3722 3723 3724
    /*
      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.
    */
3725
    if ((res= mysql_stmt_result_metadata(stmt)) != NULL)
unknown's avatar
unknown committed
3726
    {
3727 3728 3729
      /* Take the column count from meta info */
      MYSQL_FIELD *fields= mysql_fetch_fields(res);
      uint num_fields= mysql_num_fields(res);
3730

3731 3732
      if (display_metadata)
	append_metadata(ds, fields, num_fields);
3733

3734 3735
      if (!display_result_vertically)
	append_table_headings(ds, fields, num_fields);
3736

unknown's avatar
unknown committed
3737
      append_stmt_result(ds, stmt, fields, num_fields);
3738

3739
      mysql_free_result(res);     /* Free normal result set with meta data */
3740

unknown's avatar
unknown committed
3741 3742
      /* Clear prepare warnings */
      dynstr_set(&ds_prepare_warnings, NULL);
unknown's avatar
unknown committed
3743 3744
    }
    else
3745
    {
3746 3747 3748
      /*
	This is a query without resultset
      */
3749 3750
    }

3751
    if (!disable_warnings)
3752
    {
unknown's avatar
unknown committed
3753
      /* Get the warnings from execute */
3754

unknown's avatar
unknown committed
3755 3756
      /* Append warnings to ds - if there are any */
      if (append_warnings(&ds_execute_warnings, mysql) ||
3757 3758
	  ds_prepare_warnings.length ||
	  ds_warnings->length)
3759
      {
unknown's avatar
unknown committed
3760
	dynstr_append_mem(ds, "Warnings:\n", 10);
3761 3762 3763
	if (ds_warnings->length)
	  dynstr_append_mem(ds, ds_warnings->str,
			    ds_warnings->length);
unknown's avatar
unknown committed
3764 3765 3766 3767 3768 3769
	if (ds_prepare_warnings.length)
	  dynstr_append_mem(ds, ds_prepare_warnings.str,
			    ds_prepare_warnings.length);
	if (ds_execute_warnings.length)
	  dynstr_append_mem(ds, ds_execute_warnings.str,
			    ds_execute_warnings.length);
3770 3771 3772 3773
      }
    }

    if (!disable_info)
unknown's avatar
unknown committed
3774
      append_info(ds, mysql_affected_rows(mysql), mysql_info(mysql));
3775 3776 3777 3778 3779

  }

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

3781
  if (!disable_warnings)
unknown's avatar
unknown committed
3782
  {
3783
    dynstr_free(&ds_prepare_warnings);
unknown's avatar
unknown committed
3784 3785
    dynstr_free(&ds_execute_warnings);
  }
3786 3787

  /*
3788 3789 3790
    We save the return code (mysql_stmt_errno(stmt)) from the last call sent
    to the server into the mysqltest builtin variable $mysql_errno. This
    variable then can be used from the test case itself.
3791
  */
3792
  var_set_errno(mysql_stmt_errno(stmt));
unknown's avatar
unknown committed
3793
#ifndef BUG15518_FIXED
3794
  mysql_stmt_close(stmt);
unknown's avatar
unknown committed
3795 3796
  cur_con->stmt= NULL;
#endif
3797
  DBUG_VOID_RETURN;
3798 3799 3800
}


3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833

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

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

  MYSQL* mysql;
  DBUG_ENTER("util_query");

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

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

    cur_con->util_mysql= mysql;
  }

  return mysql_query(mysql, query);
}



3834 3835
/*
  Run query
3836

3837 3838 3839
  flags control the phased/stages of query execution to be performed
  if QUERY_SEND bit is on, the query will be sent. If QUERY_REAP is on
  the result will be read - for regular query, both bits must be on
3840

3841 3842 3843 3844
  SYNPOSIS
  run_query
  mysql - mysql handle
  command - currrent command pointer
unknown's avatar
unknown committed
3845

3846
*/
3847

3848
static void run_query(MYSQL *mysql, struct st_query *command, int flags)
3849
{
3850
  DYNAMIC_STRING *ds;
3851
  DYNAMIC_STRING ds_result;
3852
  DYNAMIC_STRING ds_warnings;
3853 3854 3855
  DYNAMIC_STRING eval_query;
  char *query;
  int query_len;
3856 3857
  my_bool view_created= 0, sp_created= 0;
  my_bool complete_query= ((flags & QUERY_SEND) && (flags & QUERY_REAP));
3858

3859 3860
  init_dynamic_string(&ds_warnings, NULL, 0, 256);

3861 3862 3863 3864
  /*
    Evaluate query if this is an eval command
   */
  if (command->type == Q_EVAL)
3865
  {
3866 3867 3868 3869
    init_dynamic_string(&eval_query, "", 16384, 65536);
    do_eval(&eval_query, command->query);
    query = eval_query.str;
    query_len = eval_query.length;
3870 3871 3872
  }
  else
  {
3873 3874
    query = command->query;
    query_len = strlen(query);
3875 3876
  }

3877
  /*
unknown's avatar
unknown committed
3878
    When command->record_file is set the output of _this_ query
3879 3880 3881 3882 3883
    should be compared with an already existing file
    Create a temporary dynamic string to contain the output from
    this query.
   */
  if (command->record_file[0])
3884
  {
3885 3886
    init_dynamic_string(&ds_result, "", 16384, 65536);
    ds= &ds_result;
3887 3888 3889 3890
  }
  else
    ds= &ds_res;

unknown's avatar
unknown committed
3891 3892
  /*
     Log the query into the output buffer
3893
  */
3894
  if (!disable_query_log && (flags & QUERY_SEND))
3895
  {
3896
    replace_dynstr_append_mem(ds, query, query_len);
3897 3898 3899 3900
    dynstr_append_mem(ds, delimiter, delimiter_length);
    dynstr_append_mem(ds, "\n", 1);
  }

3901 3902 3903
  if (view_protocol_enabled &&
      complete_query &&
      match_re(&view_re, query))
3904
  {
3905
    /*
3906
       Create the query as a view.
unknown's avatar
unknown committed
3907
       Use replace since view can exist from a failed mysqltest run
3908
    */
unknown's avatar
unknown committed
3909 3910 3911
    DYNAMIC_STRING query_str;
    init_dynamic_string(&query_str,
			"CREATE OR REPLACE VIEW mysqltest_tmp_v AS ",
3912 3913
			query_len+64, 256);
    dynstr_append_mem(&query_str, query, query_len);
3914
    if (util_query(mysql, query_str.str))
3915 3916 3917 3918 3919
    {
      /*
	Failed to create the view, this is not fatal
	just run the query the normal way
       */
unknown's avatar
unknown committed
3920
      DBUG_PRINT("view_create_error",
3921 3922
		 ("Failed to create view '%s': %d: %s", query_str.str,
		  mysql_errno(mysql), mysql_error(mysql)));
3923

unknown's avatar
unknown committed
3924 3925 3926
      /* Log error to create view */
      verbose_msg("Failed to create view '%s' %d: %s", query_str.str,
		  mysql_errno(mysql), mysql_error(mysql));
3927 3928 3929 3930 3931 3932 3933
    }
    else
    {
      /*
	Yes, it was possible to create this query as a view
       */
      view_created= 1;
unknown's avatar
unknown committed
3934
      query= (char*)"SELECT * FROM mysqltest_tmp_v";
3935
      query_len = strlen(query);
3936

3937 3938 3939 3940
      /*
	 Collect warnings from create of the view that should otherwise
         have been produced when the SELECT was executed
      */
3941
      append_warnings(&ds_warnings, cur_con->util_mysql);
3942 3943
    }

3944
    dynstr_free(&query_str);
3945 3946 3947

  }

3948 3949 3950
  if (sp_protocol_enabled &&
      complete_query &&
      match_re(&sp_re, query))
3951
  {
3952
    /*
3953
      Create the query as a stored procedure
unknown's avatar
unknown committed
3954
      Drop first since sp can exist from a failed mysqltest run
3955
    */
3956
    DYNAMIC_STRING query_str;
unknown's avatar
unknown committed
3957
    init_dynamic_string(&query_str,
3958
			"DROP PROCEDURE IF EXISTS mysqltest_tmp_sp;",
3959
			query_len+64, 256);
3960
    util_query(mysql, query_str.str);
3961 3962
    dynstr_set(&query_str, "CREATE PROCEDURE mysqltest_tmp_sp()\n");
    dynstr_append_mem(&query_str, query, query_len);
3963
    if (util_query(mysql, query_str.str))
3964
    {
3965
      /*
unknown's avatar
unknown committed
3966
	Failed to create the stored procedure for this query,
3967 3968 3969 3970 3971
	this is not fatal just run the query the normal way
      */
      DBUG_PRINT("sp_create_error",
		 ("Failed to create sp '%s': %d: %s", query_str.str,
		  mysql_errno(mysql), mysql_error(mysql)));
unknown's avatar
unknown committed
3972 3973 3974 3975

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

3977
    }
3978
    else
3979
    {
3980
      sp_created= 1;
unknown's avatar
unknown committed
3981 3982

      query= (char*)"CALL mysqltest_tmp_sp()";
3983
      query_len = strlen(query);
3984
    }
3985
    dynstr_free(&query_str);
3986 3987 3988
  }

  /*
3989
    Find out how to run this query
3990

unknown's avatar
unknown committed
3991
    Always run with normal C API if it's not a complete
3992
    SEND + REAP
3993

3994
    If it is a '?' in the query it may be a SQL level prepared
3995
    statement already and we can't do it twice
3996
  */
3997
  if (ps_protocol_enabled &&
3998 3999
      complete_query &&
      match_re(&ps_re, query))
4000
    run_query_stmt(mysql, command, query, query_len, ds, &ds_warnings);
4001
  else
4002 4003
    run_query_normal(mysql, command, flags, query, query_len,
		     ds, &ds_warnings);
unknown's avatar
unknown committed
4004

4005
  if (sp_created)
4006
  {
4007
    if (util_query(mysql, "DROP PROCEDURE mysqltest_tmp_sp "))
4008
      die("Failed to drop sp: %d: %s", mysql_errno(mysql), mysql_error(mysql));
4009 4010
  }

4011
  if (view_created)
4012
  {
4013
    if (util_query(mysql, "DROP VIEW mysqltest_tmp_v "))
unknown's avatar
unknown committed
4014
      die("Failed to drop view: %d: %s",
4015
	  mysql_errno(mysql), mysql_error(mysql));
4016 4017
  }

unknown's avatar
unknown committed
4018
  if (command->record_file[0])
4019 4020
  {

unknown's avatar
unknown committed
4021 4022
    /* A result file was specified for _this_ query  */
    if (record)
4023
    {
unknown's avatar
unknown committed
4024 4025 4026 4027 4028
      /*
	 Recording in progress
         Dump the output from _this_ query to the specified record_file
      */
      str_to_file(command->record_file, ds->str, ds->length);
4029

unknown's avatar
unknown committed
4030
    } else {
4031

unknown's avatar
unknown committed
4032 4033 4034 4035 4036
      /*
	The output from _this_ query should be checked against an already
	existing file which has been specified using --require or --result
      */
      check_result(ds, command->record_file, command->require_file);
4037 4038 4039
    }
  }

4040
  dynstr_free(&ds_warnings);
4041 4042
  if (ds == &ds_result)
    dynstr_free(&ds_result);
4043
  if (command->type == Q_EVAL)
4044 4045 4046 4047 4048
    dynstr_free(&eval_query);
}


/****************************************************************************\
4049
 *  Functions to detect different SQL statements
4050 4051
\****************************************************************************/

4052
static char *re_eprint(int err)
4053
{
4054
  static char epbuf[100];
unknown's avatar
unknown committed
4055
  size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL,
4056 4057 4058
			  epbuf, sizeof(epbuf));
  assert(len <= sizeof(epbuf));
  return(epbuf);
4059 4060
}

4061
static void init_re_comp(my_regex_t *re, const char* str)
4062
{
4063 4064 4065
  int err= my_regcomp(re, str, (REG_EXTENDED | REG_ICASE | REG_NOSUB),
                      &my_charset_latin1);
  if (err)
4066
  {
4067 4068
    char erbuf[100];
    int len= my_regerror(err, re, erbuf, sizeof(erbuf));
unknown's avatar
unknown committed
4069
    die("error %s, %d/%d `%s'\n",
4070
	re_eprint(err), len, (int)sizeof(erbuf), erbuf);
4071 4072 4073
  }
}

4074
static void init_re(void)
4075
{
unknown's avatar
unknown committed
4076 4077
  /*
     Filter for queries that can be run using the
4078 4079
     MySQL Prepared Statements C API
  */
4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093
  const char *ps_re_str =
    "^("
    "[[:space:]]*REPLACE[[:space:]]|"
    "[[:space:]]*INSERT[[:space:]]|"
    "[[:space:]]*UPDATE[[:space:]]|"
    "[[:space:]]*DELETE[[:space:]]|"
    "[[:space:]]*SELECT[[:space:]]|"
    "[[:space:]]*CREATE[[:space:]]+TABLE[[:space:]]|"
    "[[:space:]]*DO[[:space:]]|"
    "[[:space:]]*SET[[:space:]]+OPTION[[:space:]]|"
    "[[:space:]]*DELETE[[:space:]]+MULTI[[:space:]]|"
    "[[:space:]]*UPDATE[[:space:]]+MULTI[[:space:]]|"
    "[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])";

unknown's avatar
unknown committed
4094 4095
  /*
     Filter for queries that can be run using the
4096 4097 4098 4099
     Stored procedures
  */
  const char *sp_re_str =ps_re_str;

unknown's avatar
unknown committed
4100
  /*
4101 4102 4103 4104 4105 4106 4107 4108 4109
     Filter for queries that can be run as views
  */
  const char *view_re_str =
    "^("
    "[[:space:]]*SELECT[[:space:]])";

  init_re_comp(&ps_re, ps_re_str);
  init_re_comp(&sp_re, sp_re_str);
  init_re_comp(&view_re, view_re_str);
4110 4111 4112
}


4113
static int match_re(my_regex_t *re, char *str)
4114
{
4115
  int err= my_regexec(re, str, (size_t)0, NULL, 0);
4116 4117 4118 4119 4120

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

4122 4123
  {
    char erbuf[100];
4124 4125 4126
    int len= my_regerror(err, re, erbuf, sizeof(erbuf));
    die("error %s, %d/%d `%s'\n",
	re_eprint(err), len, (int)sizeof(erbuf), erbuf);
4127
  }
4128
  return 0;
4129 4130
}

4131
static void free_re(void)
4132
{
unknown's avatar
unknown committed
4133
  my_regfree(&ps_re);
4134 4135
  my_regfree(&sp_re);
  my_regfree(&view_re);
unknown's avatar
unknown committed
4136
  my_regex_end();
4137 4138 4139 4140
}

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

4141
void get_query_type(struct st_query* q)
4142
{
4143 4144
  char save;
  uint type;
unknown's avatar
unknown committed
4145 4146
  DBUG_ENTER("get_query_type");

4147
  if (!parsing_disabled && *q->query == '}')
4148 4149
  {
    q->type = Q_END_BLOCK;
unknown's avatar
unknown committed
4150
    DBUG_VOID_RETURN;
4151 4152
  }
  if (q->type != Q_COMMENT_WITH_COMMAND)
4153
    q->type= parsing_disabled ? Q_COMMENT : Q_QUERY;
4154

4155 4156
  save=q->query[q->first_word_len];
  q->query[q->first_word_len]=0;
4157
  type=find_type(q->query, &command_typelib, 1+2);
4158
  q->query[q->first_word_len]=save;
4159
  if (type > 0)
4160
  {
unknown's avatar
unknown committed
4161
    q->type=(enum enum_commands) type;		/* Found command */
4162 4163 4164 4165 4166 4167 4168 4169
    /*
      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;
  }
4170
  else if (q->type == Q_COMMENT_WITH_COMMAND &&
unknown's avatar
unknown committed
4171
	   q->first_word_len &&
4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186
           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
4187
  DBUG_VOID_RETURN;
4188
}
unknown's avatar
unknown committed
4189

unknown's avatar
unknown committed
4190

unknown's avatar
unknown committed
4191
static byte *get_var_key(const byte* var, uint* len,
unknown's avatar
unknown committed
4192
			 my_bool __attribute__((unused)) t)
unknown's avatar
unknown committed
4193 4194 4195 4196 4197 4198 4199
{
  register char* key;
  key = ((VAR*)var)->name;
  *len = ((VAR*)var)->name_len;
  return (byte*)key;
}

unknown's avatar
unknown committed
4200
static VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
unknown's avatar
unknown committed
4201 4202 4203
		     int val_len)
{
  int val_alloc_len;
unknown's avatar
unknown committed
4204
  VAR *tmp_var;
4205
  if (!name_len && name)
unknown's avatar
unknown committed
4206
    name_len = strlen(name);
4207
  if (!val_len && val)
unknown's avatar
unknown committed
4208 4209
    val_len = strlen(val) ;
  val_alloc_len = val_len + 16; /* room to grow */
4210
  if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var)
unknown's avatar
unknown committed
4211
						 + name_len+1, MYF(MY_WME))))
unknown's avatar
unknown committed
4212
    die("Out of memory");
unknown's avatar
unknown committed
4213

4214
  tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0;
4215
  tmp_var->alloced = (v == 0);
4216

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

4220 4221
  if (name)
    strmake(tmp_var->name, name, name_len);
4222
  if (val)
4223
    strmake(tmp_var->str_val, val, val_len);
unknown's avatar
unknown committed
4224 4225 4226
  tmp_var->name_len = name_len;
  tmp_var->str_val_len = val_len;
  tmp_var->alloced_len = val_alloc_len;
4227
  tmp_var->int_val = (val) ? atoi(val) : 0;
unknown's avatar
unknown committed
4228 4229 4230 4231
  tmp_var->int_dirty = 0;
  return tmp_var;
}

unknown's avatar
unknown committed
4232
static void var_free(void *v)
unknown's avatar
unknown committed
4233
{
unknown's avatar
unknown committed
4234
  my_free(((VAR*) v)->str_val, MYF(MY_WME));
unknown's avatar
unknown committed
4235 4236
  if (((VAR*)v)->alloced)
   my_free((char*) v, MYF(MY_WME));
unknown's avatar
unknown committed
4237 4238 4239
}


4240
static VAR* var_from_env(const char *name, const char *def_val)
unknown's avatar
unknown committed
4241
{
unknown's avatar
unknown committed
4242 4243
  const char *tmp;
  VAR *v;
4244
  if (!(tmp = getenv(name)))
unknown's avatar
unknown committed
4245
    tmp = def_val;
unknown's avatar
unknown committed
4246

4247
  v = var_init(0, name, strlen(name), tmp, strlen(tmp));
unknown's avatar
SCRUM  
unknown committed
4248
  my_hash_insert(&var_hash, (byte*)v);
4249
  return v;
4250
}
unknown's avatar
unknown committed
4251

4252

unknown's avatar
unknown committed
4253
static void init_var_hash(MYSQL *mysql)
unknown's avatar
unknown committed
4254
{
unknown's avatar
unknown committed
4255
  VAR *v;
4256
  DBUG_ENTER("init_var_hash");
unknown's avatar
unknown committed
4257
  if (hash_init(&var_hash, charset_info,
unknown's avatar
unknown committed
4258
                1024, 0, 0, get_var_key, var_free, MYF(0)))
unknown's avatar
unknown committed
4259
    die("Variable hash initialization failed");
4260 4261
  my_hash_insert(&var_hash, (byte*) var_init(0,"BIG_TEST", 0,
                                             (opt_big_test) ? "1" : "0", 0));
unknown's avatar
unknown committed
4262
  v= var_init(0,"MAX_TABLES", 0, (sizeof(ulong) == 4) ? "31" : "62",0);
unknown's avatar
SCRUM  
unknown committed
4263
  my_hash_insert(&var_hash, (byte*) v);
unknown's avatar
unknown committed
4264
  v= var_init(0,"SERVER_VERSION", 0, mysql_get_server_info(mysql), 0);
4265
  my_hash_insert(&var_hash, (byte*) v);  v= var_init(0,"DB", 2, db, 0);
unknown's avatar
unknown committed
4266
  my_hash_insert(&var_hash, (byte*) v);
4267
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
4268
}
4269

4270

unknown's avatar
unknown committed
4271
int main(int argc, char **argv)
unknown's avatar
unknown committed
4272
{
unknown's avatar
unknown committed
4273
  struct st_query *q;
4274 4275
  my_bool require_file=0, q_send_flag=0, abort_flag= 0,
          query_executed= 0;
4276
  char save_file[FN_REFLEN];
4277
  MY_STAT res_info;
4278 4279
  MY_INIT(argv[0]);

unknown's avatar
unknown committed
4280 4281 4282
  /* Use all time until exit if no explicit 'start_timer' */
  timer_start= timer_now();

4283
  save_file[0]=0;
4284
  TMPDIR[0]=0;
4285 4286

  /* Init cons */
unknown's avatar
unknown committed
4287 4288 4289 4290
  memset(cons, 0, sizeof(cons));
  cons_end = cons + MAX_CONS;
  next_con = cons + 1;
  cur_con = cons;
unknown's avatar
unknown committed
4291

4292
  /* Init file stack */
unknown's avatar
unknown committed
4293
  memset(file_stack, 0, sizeof(file_stack));
4294 4295
  file_stack_end= file_stack + MAX_INCLUDE_DEPTH - 1;
  cur_file= file_stack;
4296

4297
  /* Init block stack */
4298
  memset(block_stack, 0, sizeof(block_stack));
4299
  block_stack_end= block_stack + BLOCK_STACK_DEPTH - 1;
unknown's avatar
unknown committed
4300 4301 4302 4303
  cur_block= block_stack;
  cur_block->ok= TRUE; /* Outer block should always be executed */
  cur_block->cmd= cmd_none;

4304 4305 4306 4307 4308
  my_init_dynamic_array(&q_lines, sizeof(struct st_query*), INIT_Q_LINES,
		     INIT_Q_LINES);

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

4309
  init_dynamic_string(&ds_res, "", 0, 65536);
unknown's avatar
unknown committed
4310
  parse_args(argc, argv);
4311 4312

  DBUG_PRINT("info",("result_file: '%s'", result_file ? result_file : ""));
4313 4314 4315
  if (mysql_server_init(embedded_server_arg_count,
			embedded_server_args,
			(char**) embedded_server_groups))
unknown's avatar
unknown committed
4316
    die("Can't initialize MySQL server");
4317
  if (cur_file == file_stack && cur_file->file == 0)
4318
  {
4319 4320
    cur_file->file= stdin;
    cur_file->file_name= my_strdup("<stdin>", MYF(MY_WME));
4321
    cur_file->lineno= 1;
4322
  }
4323 4324 4325
#ifndef EMBEDDED_LIBRARY
  if (manager_host)
    init_manager();
4326
#endif
4327 4328 4329 4330 4331 4332 4333
  init_re();
  ps_protocol_enabled= ps_protocol;
  sp_protocol_enabled= sp_protocol;
  view_protocol_enabled= view_protocol;
  cursor_protocol_enabled= cursor_protocol;
  /* Cursor protcol implies ps protocol */
  if (cursor_protocol_enabled)
4334
    ps_protocol_enabled= 1;
4335

4336
  if (!( mysql_init(&cur_con->mysql)))
unknown's avatar
unknown committed
4337
    die("Failed in mysql_init()");
unknown's avatar
unknown committed
4338 4339
  if (opt_compress)
    mysql_options(&cur_con->mysql,MYSQL_OPT_COMPRESS,NullS);
4340
  mysql_options(&cur_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
unknown's avatar
unknown committed
4341
  mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name);
unknown's avatar
unknown committed
4342

unknown's avatar
unknown committed
4343 4344 4345 4346 4347
#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
4348

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

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

unknown's avatar
unknown committed
4356 4357
  init_var_hash(&cur_con->mysql);

4358 4359 4360 4361 4362
  /*
    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
  */
4363 4364
  var_set_errno(-1);

unknown's avatar
unknown committed
4365
  while (!abort_flag && !read_query(&q))
4366 4367 4368 4369
  {
    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
4370
    if (cur_block->ok)
unknown's avatar
unknown committed
4371
    {
4372
      q->last_argument= q->first_argument;
4373 4374
      processed = 1;
      switch (q->type) {
4375
      case Q_CONNECT:
4376
        do_connect(q);
4377
        break;
4378
      case Q_CONNECTION: select_connection(q); break;
unknown's avatar
unknown committed
4379
      case Q_DISCONNECT:
4380
      case Q_DIRTY_CLOSE:
unknown's avatar
unknown committed
4381
	close_connection(q); break;
4382
      case Q_RPL_PROBE: do_rpl_probe(q); break;
4383
      case Q_ENABLE_RPL_PARSE:	 do_enable_rpl_parse(q); break;
unknown's avatar
unknown committed
4384
      case Q_DISABLE_RPL_PARSE:  do_disable_rpl_parse(q); break;
4385
      case Q_ENABLE_QUERY_LOG:   disable_query_log=0; break;
unknown's avatar
unknown committed
4386
      case Q_DISABLE_QUERY_LOG:  disable_query_log=1; break;
4387 4388
      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
4389 4390
      case Q_ENABLE_RESULT_LOG:  disable_result_log=0; break;
      case Q_DISABLE_RESULT_LOG: disable_result_log=1; break;
4391 4392
      case Q_ENABLE_WARNINGS:    disable_warnings=0; break;
      case Q_DISABLE_WARNINGS:   disable_warnings=1; break;
4393 4394
      case Q_ENABLE_PS_WARNINGS:    disable_ps_warnings=0; break;
      case Q_DISABLE_PS_WARNINGS:   disable_ps_warnings=1; break;
4395 4396
      case Q_ENABLE_INFO:        disable_info=0; break;
      case Q_DISABLE_INFO:       disable_info=1; break;
4397
      case Q_ENABLE_METADATA:    display_metadata=1; break;
4398
      case Q_DISABLE_METADATA:   display_metadata=0; break;
4399
      case Q_SOURCE: do_source(q); break;
4400 4401
      case Q_SLEEP: do_sleep(q, 0); break;
      case Q_REAL_SLEEP: do_sleep(q, 1); break;
4402
      case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(q); break;
4403
      case Q_REQUIRE_MANAGER: do_require_manager(q); break;
4404
#ifndef EMBEDDED_LIBRARY
4405 4406
      case Q_SERVER_START: do_server_start(q); break;
      case Q_SERVER_STOP: do_server_stop(q); break;
4407
#endif
4408 4409
      case Q_INC: do_modify_var(q, "inc", DO_INC); break;
      case Q_DEC: do_modify_var(q, "dec", DO_DEC); break;
4410
      case Q_ECHO: do_echo(q); query_executed= 1; break;
4411
      case Q_SYSTEM: do_system(q); break;
4412 4413 4414
      case Q_DELIMITER:
	strmake(delimiter, q->first_argument, sizeof(delimiter) - 1);
	delimiter_length= strlen(delimiter);
4415
        q->last_argument= q->first_argument+delimiter_length;
4416
	break;
4417 4418 4419 4420 4421 4422
      case Q_DISPLAY_VERTICAL_RESULTS:
        display_result_vertically= TRUE;
        break;
      case Q_DISPLAY_HORIZONTAL_RESULTS:
	display_result_vertically= FALSE;
        break;
4423
      case Q_LET: do_let(q); break;
4424
      case Q_EVAL_RESULT:
4425
        eval_result = 1; break;
4426
      case Q_EVAL:
4427
	if (q->query == q->query_buf)
unknown's avatar
unknown committed
4428
        {
unknown's avatar
unknown committed
4429
	  q->query= q->first_argument;
unknown's avatar
unknown committed
4430 4431
          q->first_word_len= 0;
        }
4432
	/* fall through */
4433
      case Q_QUERY_VERTICAL:
4434
      case Q_QUERY_HORIZONTAL:
4435 4436 4437 4438 4439
      {
	my_bool old_display_result_vertically= display_result_vertically;
	/* 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;
4440
	display_result_vertically= (q->type==Q_QUERY_VERTICAL);
4441 4442 4443 4444 4445 4446
	if (save_file[0])
	{
	  strmov(q->record_file,save_file);
	  q->require_file=require_file;
	  save_file[0]=0;
	}
4447
	run_query(&cur_con->mysql, q, QUERY_REAP|QUERY_SEND);
4448
	display_result_vertically= old_display_result_vertically;
4449
        q->last_argument= q->end;
4450
        query_executed= 1;
4451 4452
	break;
      }
4453
      case Q_QUERY:
4454
      case Q_REAP:
4455
      {
4456 4457 4458 4459 4460
	/*
	  We read the result always regardless of the mode for both full
	  query and read-result only (reap)
	*/
	int flags = QUERY_REAP;
4461
	if (q->type != Q_REAP) /* for a full query, enable the send stage */
unknown's avatar
unknown committed
4462
	  flags |= QUERY_SEND;
unknown's avatar
unknown committed
4463 4464 4465 4466 4467
	if (q_send_flag)
	{
	  flags= QUERY_SEND;
	  q_send_flag=0;
	}
4468
	if (save_file[0])
4469
	{
4470 4471 4472
	  strmov(q->record_file,save_file);
	  q->require_file=require_file;
	  save_file[0]=0;
4473
	}
4474
	run_query(&cur_con->mysql, q, flags);
4475
	query_executed= 1;
4476
        q->last_argument= q->end;
unknown's avatar
unknown committed
4477
	break;
4478
      }
unknown's avatar
unknown committed
4479
      case Q_SEND:
unknown's avatar
unknown committed
4480 4481 4482 4483 4484 4485 4486 4487
	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)
4488
	  q->query += q->first_word_len;
4489 4490 4491 4492 4493
	/*
	  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.
4494
	 */
4495
	run_query(&cur_con->mysql, q, QUERY_SEND);
4496
	query_executed= 1;
4497
        q->last_argument= q->end;
unknown's avatar
unknown committed
4498
	break;
4499 4500 4501 4502
      case Q_RESULT:
	get_file_name(save_file,q);
	require_file=0;
	break;
unknown's avatar
unknown committed
4503
      case Q_ERROR:
4504
        global_expected_errors=get_errcodes(global_expected_errno,q);
unknown's avatar
unknown committed
4505
	break;
4506 4507 4508 4509
      case Q_REQUIRE:
	get_file_name(save_file,q);
	require_file=1;
	break;
unknown's avatar
unknown committed
4510 4511 4512
      case Q_REPLACE:
	get_replace(q);
	break;
4513 4514 4515
      case Q_REPLACE_COLUMN:
	get_replace_column(q);
	break;
4516 4517
      case Q_SAVE_MASTER_POS: do_save_master_pos(); break;
      case Q_SYNC_WITH_MASTER: do_sync_with_master(q); break;
4518 4519 4520 4521
      case Q_SYNC_SLAVE_WITH_MASTER:
      {
	do_save_master_pos();
	if (*q->first_argument)
4522
	  select_connection(q);
4523
	else
4524 4525
	  select_connection_name("slave");
	do_sync_with_master2(0);
4526 4527
	break;
      }
4528
      case Q_COMMENT:				/* Ignore row */
4529
      case Q_COMMENT_WITH_COMMAND:
4530
        q->last_argument= q->end;
4531
	break;
4532 4533 4534
      case Q_PING:
	(void) mysql_ping(&cur_con->mysql);
	break;
4535
      case Q_EXEC:
unknown's avatar
unknown committed
4536
	do_exec(q);
4537
	query_executed= 1;
4538
	break;
unknown's avatar
unknown committed
4539 4540 4541 4542 4543 4544 4545 4546 4547
      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;
4548
      case Q_CHARACTER_SET:
unknown's avatar
unknown committed
4549 4550
	set_charset(q);
	break;
4551 4552 4553 4554 4555 4556
      case Q_DISABLE_PS_PROTOCOL:
        ps_protocol_enabled= 0;
        break;
      case Q_ENABLE_PS_PROTOCOL:
        ps_protocol_enabled= ps_protocol;
        break;
4557
      case Q_DISABLE_RECONNECT:
4558 4559 4560
      {
        my_bool reconnect= 0;
        mysql_options(&cur_con->mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
4561
        break;
4562
      }
4563
      case Q_ENABLE_RECONNECT:
4564 4565 4566
      {
        my_bool reconnect= 1;
        mysql_options(&cur_con->mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
4567
        break;
4568
      }
4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579
      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;
4580

unknown's avatar
unknown committed
4581 4582 4583
      case Q_EXIT:
        abort_flag= 1;
        break;
4584 4585 4586 4587

      default:
        processed= 0;
        break;
4588 4589
      }
    }
4590

4591 4592
    if (!processed)
    {
4593
      current_line_inc= 0;
unknown's avatar
unknown committed
4594
      switch (q->type) {
unknown's avatar
unknown committed
4595 4596
      case Q_WHILE: do_block(cmd_while, q); break;
      case Q_IF: do_block(cmd_if, q); break;
4597 4598 4599
      case Q_END_BLOCK: do_done(q); break;
      default: current_line_inc = 1; break;
      }
unknown's avatar
unknown committed
4600
    }
4601 4602
    else
      check_eol_junk(q->last_argument);
unknown's avatar
unknown committed
4603

4604 4605 4606 4607 4608 4609 4610 4611 4612
    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
4613

4614 4615 4616
    parser.current_line += current_line_inc;
  }

4617 4618
  start_lineno= 0;

4619
  /*
unknown's avatar
unknown committed
4620
    The whole test has been executed _sucessfully_
4621 4622
    Time to compare result or save it to record file
    The entire output from test is now kept in ds_res
unknown's avatar
unknown committed
4623
  */
4624
  if (ds_res.length)
4625
  {
4626 4627
    if (result_file)
    {
4628 4629 4630 4631 4632
      if (record)
      {
	/* Dump the output from test to result file */
	str_to_file(result_file, ds_res.str, ds_res.length);
      }
4633
      else
4634
      {
unknown's avatar
unknown committed
4635 4636 4637 4638
	/* Check that the output from test is equal to result file
	   - detect missing result file
	   - detect zero size result file
	 */
4639 4640
	check_result(&ds_res, result_file, 0);
      }
4641
    }
4642
    else
4643
    {
unknown's avatar
unknown committed
4644
      /* No result_file specified to compare with, print to stdout */
4645 4646
      printf("%s", ds_res.str);
    }
4647
  }
unknown's avatar
unknown committed
4648
  else
unknown's avatar
unknown committed
4649
  {
unknown's avatar
unknown committed
4650
    die("The test didn't produce any output");
unknown's avatar
unknown committed
4651
  }
4652

unknown's avatar
unknown committed
4653
  if (!query_executed && result_file && my_stat(result_file, &res_info, 0))
unknown's avatar
unknown committed
4654
  {
unknown's avatar
unknown committed
4655 4656 4657 4658 4659 4660 4661 4662
    /*
      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.
    */
    die("No queries executed but result file found!");
unknown's avatar
unknown committed
4663
  }
4664

unknown's avatar
unknown committed
4665

4666
  dynstr_free(&ds_res);
4667

unknown's avatar
unknown committed
4668 4669
  if (!got_end_timer)
    timer_output();				/* No end_timer cmd, end it */
4670
  free_used_memory();
4671
  my_end(MY_CHECK_ERROR);
4672 4673 4674 4675 4676 4677

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

4680

4681 4682 4683 4684 4685 4686
/*
  Read arguments for embedded server and put them into
  embedded_server_args_count and embedded_server_args[]
*/


unknown's avatar
unknown committed
4687
static int read_server_arguments(const char *name)
4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703
{
  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
4704
  if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725
    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
4726

unknown's avatar
unknown committed
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
/****************************************************************************\
 *
 *  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)
  {
4754
    char buf[32], *end;
unknown's avatar
unknown committed
4755
    ulonglong timer= timer_now() - timer_start;
4756 4757
    end= longlong2str(timer, buf, 10);
    str_to_file(timer_file,buf, (int) (end-buf));
unknown's avatar
unknown committed
4758 4759 4760 4761 4762 4763 4764 4765
  }
}

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

unknown's avatar
unknown committed
4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778
/****************************************************************************
* 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 {
4779
  bool	 found;
unknown's avatar
unknown committed
4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 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
  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 {
4885 4886
  uint	*bits;				/* Pointer to used sets */
  short next[LAST_CHAR_CODE];		/* Pointer to next sets */
unknown's avatar
unknown committed
4887 4888
  uint	found_len;			/* Best match to date */
  int	found_offset;
4889 4890
  uint	table_offset;
  uint	size_of_bits;			/* For convinience */
unknown's avatar
unknown committed
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
} 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);
4919 4920
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
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 4946 4947 4948 4949 4950 4951 4952 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
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] == '^')
    {
4997
      internal_set_bit(start_states,states+1);
unknown's avatar
unknown committed
4998 4999 5000 5001 5002 5003 5004 5005
      if (!from[i][2])
      {
	start_states->table_offset=i;
	start_states->found_offset=1;
      }
    }
    else if (from[i][0] == '\\' && from[i][1] == '$')
    {
5006 5007
      internal_set_bit(start_states,states);
      internal_set_bit(word_states,states);
unknown's avatar
unknown committed
5008 5009 5010 5011 5012 5013 5014 5015
      if (!from[i][2] && start_states->table_offset == (uint) ~0)
      {
	start_states->table_offset=i;
	start_states->found_offset=0;
      }
    }
    else
    {
5016
      internal_set_bit(word_states,states);
unknown's avatar
unknown committed
5017
      if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2]))
5018
	internal_set_bit(start_states,states+1);
unknown's avatar
unknown committed
5019
      else
5020
	internal_set_bit(start_states,states);
unknown's avatar
unknown committed
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 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125
    }
    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)
5126
	      internal_set_bit(new_set,i+1);		/* To next set */
unknown's avatar
unknown committed
5127
	    else
5128
	      internal_set_bit(new_set,i);
unknown's avatar
unknown committed
5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144
	  }
	}
	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)))
5145
	      internal_clear_bit(new_set,i);
unknown's avatar
unknown committed
5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293
	    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;
}

5294
static void internal_set_bit(REP_SET *set, uint bit)
unknown's avatar
unknown committed
5295 5296 5297 5298 5299
{
  set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
  return;
}

5300
static void internal_clear_bit(REP_SET *set, uint bit)
unknown's avatar
unknown committed
5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420
{
  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 */

5421
uint replace_strings(REPLACE *rep, my_string *start,uint *max_length,
unknown's avatar
unknown committed
5422
		     const char *from)
unknown's avatar
unknown committed
5423 5424 5425
{
  reg1 REPLACE *rep_pos;
  reg2 REPLACE_STRING *rep_str;
unknown's avatar
unknown committed
5426
  my_string to,end,pos,new_str;
unknown's avatar
unknown committed
5427 5428 5429

  end=(to= *start) + *max_length-1;
  rep_pos=rep+1;
5430
  for (;;)
unknown's avatar
unknown committed
5431 5432 5433 5434 5435 5436 5437
  {
    while (!rep_pos->found)
    {
      rep_pos= rep_pos->next[(uchar) *from];
      if (to == end)
      {
	(*max_length)+=8192;
unknown's avatar
unknown committed
5438
	if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME))))
unknown's avatar
unknown committed
5439
	  return (uint) -1;
unknown's avatar
unknown committed
5440 5441
	to=new_str+(to - *start);
	end=(*start=new_str)+ *max_length-1;
unknown's avatar
unknown committed
5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452
      }
      *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
5453
	if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME))))
unknown's avatar
unknown committed
5454
	  return (uint) -1;
unknown's avatar
unknown committed
5455 5456
	to=new_str+(to - *start);
	end=(*start=new_str)+ *max_length-1;
unknown's avatar
unknown committed
5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473
      }
      *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;
}

5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507
/****************************************************************************
 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)
5508
    die("Missing argument in %s", q->query);
5509 5510 5511 5512 5513 5514 5515 5516 5517 5518

  /* 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)
5519
      die("Wrong column number to replace_column in '%s'", q->query);
5520
    if (!*from)
5521
      die("Wrong number of arguments to replace_column in '%s'", q->query);
5522 5523 5524 5525 5526 5527
    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));
5528
  q->last_argument= q->end;
5529
}
unknown's avatar
unknown committed
5530

unknown's avatar
unknown committed
5531
#if defined(__NETWARE__) || defined(__WIN__)
unknown's avatar
unknown committed
5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552
/*
  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;
5553
  char *pos;
unknown's avatar
unknown committed
5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572

  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++)
5573
        *env_pos++= *str;
unknown's avatar
unknown committed
5574 5575 5576 5577 5578
      *env_pos= 0;

      if (!(subst= getenv(env_var)))
      {
        my_free(result, MYF(0));
5579
        die("MYSQLTEST.NLM: Environment variable %s is not defined",
unknown's avatar
unknown committed
5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614
            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
5615
#ifdef __WIN__
unknown's avatar
unknown committed
5616 5617
#define popen _popen                           /* redefine for windows */
#endif
unknown's avatar
unknown committed
5618

5619
FILE *my_popen(const char *cmd, const char *mode __attribute__((unused)))
unknown's avatar
unknown committed
5620 5621
{
  char *subst_cmd;
5622 5623
  FILE *res_file;

unknown's avatar
unknown committed
5624 5625 5626 5627 5628 5629
  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
5630
#endif /* __NETWARE__ or  __WIN__*/
5631 5632