mysqltest.c 96.5 KB
Newer Older
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1
/* Copyright (C) 2000 MySQL AB
2

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

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

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
20 21 22 23
 *
 * Written by:
 *   Sasha Pachev <sasha@mysql.com>
 *   Matt Wagner  <matt@mysql.com>
24
 *   Monty
25
 *   Jani
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.2"
46

47
#include <my_global.h>
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi 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>
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
53
#include <mysqld_error.h>
54 55
#include <m_ctype.h>
#include <my_dir.h>
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
56
#include <hash.h>
57
#include <my_getopt.h>
58 59
#include <stdarg.h>
#include <sys/stat.h>
60
#include <violite.h>
kent@mysql.com's avatar
kent@mysql.com committed
61 62 63 64 65 66 67
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif

#ifndef MAXPATHLEN
#define MAXPATHLEN 256
#endif
68

69
#define MAX_QUERY     131072
monty@mysql.com's avatar
monty@mysql.com committed
70
#define MAX_VAR_NAME	256
71
#define MAX_COLUMNS	256
72
#define PAD_SIZE	128
73
#define MAX_CONS	128
74
#define MAX_INCLUDE_DEPTH 16
75
#define LAZY_GUESS_BUF_SIZE 8192
76 77
#define INIT_Q_LINES	  1024
#define MIN_VAR_ALLOC	  32
78
#define BLOCK_STACK_DEPTH  32
79
#define MAX_EXPECTED_ERRORS 10
80 81
#define QUERY_SEND  1
#define QUERY_REAP  2
82 83 84
#ifndef MYSQL_MANAGER_PORT
#define MYSQL_MANAGER_PORT 23546
#endif
kent@mysql.com's avatar
kent@mysql.com committed
85
#define MAX_SERVER_ARGS 64
86

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
87 88 89 90 91 92 93
/*
  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
94
#define MAX_CON_TRIES	5
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
95

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

100
enum {OPT_MANAGER_USER=256,OPT_MANAGER_HOST,OPT_MANAGER_PASSWD,
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
101 102 103
      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,
      OPT_SSL_CIPHER};
104

105
static int record = 0, opt_sleep=0;
106
static char *db = 0, *pass=0;
107
const char* user = 0, *host = 0, *unix_sock = 0, *opt_basedir="./";
108 109
static int port = 0;
static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0,
110
	       tty_password= 0;
111
static uint start_lineno, *lineno;
112
const char* manager_user="root",*manager_host=0;
113 114 115 116
char *manager_pass=0;
int manager_port=MYSQL_MANAGER_PORT;
int manager_wait_timeout=3;
MYSQL_MANAGER* manager=0;
117

118
static char **default_argv;
119
static const char *load_default_groups[]= { "mysqltest","client",0 };
monty@mysql.com's avatar
monty@mysql.com committed
120
static char line_buffer[MAX_DELIMITER], *line_buffer_pos= line_buffer;
121

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
122 123 124 125 126
static FILE* file_stack[MAX_INCLUDE_DEPTH];
static FILE** cur_file;
static FILE** file_stack_end;
static uint lineno_stack[MAX_INCLUDE_DEPTH];
static char TMPDIR[FN_REFLEN];
127 128
static char delimiter[MAX_DELIMITER]= DEFAULT_DELIMITER;
static uint delimiter_length= 1;
129

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
130
static int *cur_block, *block_stack_end;
131 132 133
static int block_stack[BLOCK_STACK_DEPTH];

static int block_ok_stack[BLOCK_STACK_DEPTH];
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
134
static uint global_expected_errno[MAX_EXPECTED_ERRORS], global_expected_errors;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
135
static CHARSET_INFO *charset_info= &my_charset_latin1;
136

137 138 139
static int embedded_server_arg_count=0;
static char *embedded_server_args[MAX_SERVER_ARGS];

140
static my_bool display_result_vertically= FALSE, display_metadata= FALSE;
141

kent@mysql.com's avatar
kent@mysql.com committed
142 143 144 145 146 147 148
/* 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);

149 150 151 152 153 154 155
static const char *embedded_server_groups[] = {
  "server",
  "embedded",
  "mysqltest_SERVER",
  NullS
};

156 157
DYNAMIC_ARRAY q_lines;

monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
158 159
#include "sslopt-vars.h"

160
typedef struct
161 162 163 164 165
{
  char file[FN_REFLEN];
  ulong pos;
} MASTER_POS ;

166 167 168 169 170 171
struct connection
{
  MYSQL mysql;
  char *name;
};

172 173 174 175
typedef struct
{
  int read_lines,current_line;
} PARSER;
176

177 178
MYSQL_RES *last_result=0;

179
PARSER parser;
180
MASTER_POS master_pos;
181
int *block_ok; /* set to 0 if the current block should not be executed */
182
int false_block_depth = 0;
183 184
/* if set, all results are concated and compared against this file */
const char *result_file = 0;
185

186
typedef struct
187
{
188
  char *name;
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
189
  int name_len;
190
  char *str_val;
191 192 193 194
  int str_val_len;
  int int_val;
  int alloced_len;
  int int_dirty; /* do not update string if int is updated until first read */
195
  int alloced;
196 197
} VAR;

monty@mysql.com's avatar
monty@mysql.com committed
198 199 200 201 202 203 204
#ifdef __NETWARE__
/*
  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);
205
static FILE *my_popen(const char *cmd, const char *mode);
monty@mysql.com's avatar
monty@mysql.com committed
206 207 208
#define popen(A,B) my_popen((A),(B))
#endif /* __NETWARE__ */

209 210
VAR var_reg[10];
/*Perl/shell-like variable registers */
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
211
HASH var_hash;
212 213
my_bool disable_query_log=0, disable_result_log=0, disable_warnings=0;
my_bool disable_info= 1;			/* By default off */
214

215 216 217
struct connection cons[MAX_CONS];
struct connection* cur_con, *next_con, *cons_end;

218 219 220
  /* Add new commands before Q_UNKNOWN !*/

enum enum_commands {
221
Q_CONNECTION=1,     Q_QUERY,
222
Q_CONNECT,	    Q_SLEEP, Q_REAL_SLEEP, 
223 224 225 226 227 228
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,
229 230 231
Q_SYNC_WITH_MASTER,
Q_SYNC_SLAVE_WITH_MASTER,
Q_ERROR,
232
Q_SEND,		    Q_REAP,
233
Q_DIRTY_CLOSE,	    Q_REPLACE, Q_REPLACE_COLUMN,
234 235
Q_PING,		    Q_EVAL,
Q_RPL_PROBE,	    Q_ENABLE_RPL_PARSE,
236
Q_DISABLE_RPL_PARSE, Q_EVAL_RESULT,
237
Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
238
Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
239
Q_SERVER_START, Q_SERVER_STOP,Q_REQUIRE_MANAGER,
240
Q_WAIT_FOR_SLAVE_TO_STOP,
241 242
Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
Q_ENABLE_INFO, Q_DISABLE_INFO,
243
Q_ENABLE_METADATA, Q_DISABLE_METADATA,
244
Q_EXEC, Q_DELIMITER,
245 246
Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL,
kent@mysql.com's avatar
kent@mysql.com committed
247
Q_START_TIMER, Q_END_TIMER,
248

249 250
Q_UNKNOWN,			       /* Unknown command.   */
Q_COMMENT,			       /* Comments, ignored. */
251 252 253
Q_COMMENT_WITH_COMMAND
};

254
/* this should really be called command */
255
struct st_query
256
{
257
  char *query, *query_buf,*first_argument,*end;
258
  int first_word_len;
259
  my_bool abort_on_error, require_file;
260
  uint expected_errno[MAX_EXPECTED_ERRORS];
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
261
  uint expected_errors;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
262
  char record_file[FN_REFLEN];
263
  enum enum_commands type;
264 265
};

266 267 268 269 270
const char *command_names[]=
{
  "connection",
  "query",
  "connect",
serg@serg.mylan's avatar
serg@serg.mylan committed
271 272 273 274 275 276 277
  /* the difference between sleep and real_sleep is that sleep will use
     the delay from command line (--sleep) if there is one.
     real_sleep always uses delay from it's argument.
     the logic is that sometimes delays are cpu-dependent (and --sleep
     can be used to set this delay. real_sleep is used for cpu-independent
     delays
   */
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
  "sleep",
  "real_sleep",
  "inc",
  "dec",
  "source",
  "disconnect",
  "let",
  "echo",
  "while",
  "end",
  "system",
  "result",
  "require",
  "save_master_pos",
  "sync_with_master",
293
  "sync_slave_with_master",
294 295 296 297 298
  "error",
  "send",
  "reap",
  "dirty_close",
  "replace_result",
299
  "replace_column",
300 301 302 303 304 305 306 307 308 309 310 311 312 313
  "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",
314 315 316
  "enable_warnings",
  "disable_warnings",
  "enable_info",
317
  "disable_info",
318 319
  "enable_metadata",
  "disable_metadata",
320
  "exec",
321
  "delimiter",
322
  "vertical_results",
323
  "horizontal_results",
324
  "query_vertical",
325
  "query_horizontal",
kent@mysql.com's avatar
kent@mysql.com committed
326 327
  "start_timer",
  "end_timer",
jcole@tetra.spaceapes.com's avatar
jcole@tetra.spaceapes.com committed
328
  0
329 330 331 332 333
};

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

334
DYNAMIC_STRING ds_res;
335
static void die(const char *fmt, ...);
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
336
static void init_var_hash();
337
static VAR* var_from_env(const char *, const char *);
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
338
static byte* get_var_key(const byte* rec, uint* len,
339
			 my_bool __attribute__((unused)) t);
340
static VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
341 342 343
		     int val_len);

static void var_free(void* v);
344

345 346
int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname);
void reject_dump(const char *record_file, char *buf, int size);
347

348
int close_connection(struct st_query* q);
349 350
VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw,
	     my_bool ignore_not_existing);
351 352
int eval_expr(VAR* v, const char *p, const char** p_end);
static int read_server_arguments(const char *name);
353

354
/* Definitions for replace result */
355 356 357 358 359

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. */
360
  uint	array_allocs,max_count,length,max_length;
361 362 363 364 365 366
} 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,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
367
		     uint *max_length, const char *from);
368
void free_replace();
369 370 371 372
static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name);
void free_pointer_array(POINTER_ARRAY *pa);
static int initialize_replace_buffer(void);
static void free_replace_buffer(void);
373 374 375
static void do_eval(DYNAMIC_STRING* query_eval, const char *query);
void str_to_file(const char *fname, char *str, int size);
int do_server_op(struct st_query* q,const char *op);
376 377 378 379

struct st_replace *glob_replace;
static char *out_buff;
static uint out_length;
380
static int eval_result = 0;
381

382 383 384 385 386 387 388
/* 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();

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
389
/* Disable functions that only exist in MySQL 4.0 */
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
390
#if MYSQL_VERSION_ID < 40000
391 392 393
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; }
394
my_bool mysql_rpl_probe(MYSQL *mysql __attribute__((unused))) { return 1; }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
395
#endif
396 397
static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
				      int len);
398

399 400 401 402 403 404
static void do_eval(DYNAMIC_STRING* query_eval, const char* query)
{
  const char* p;
  register char c;
  register int escaped = 0;
  VAR* v;
monty@mysql.com's avatar
monty@mysql.com committed
405
  DBUG_ENTER("do_eval");
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
406

407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
  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;
435
    }
436
  }
monty@mysql.com's avatar
monty@mysql.com committed
437
  DBUG_VOID_RETURN;
438
}
439

440

441 442
static void close_cons()
{
443
  DBUG_ENTER("close_cons");
444 445
  if (last_result)
    mysql_free_result(last_result);
446 447 448 449 450 451 452 453
  for (--next_con; next_con >= cons; --next_con)
  {
    mysql_close(&next_con->mysql);
    my_free(next_con->name, MYF(MY_ALLOW_ZERO_PTR));
  }
  DBUG_VOID_RETURN;
}

454

455 456
static void close_files()
{
457 458
  DBUG_ENTER("close_files");
  for (; cur_file != file_stack ; cur_file--)
459
  {
460
    if (*cur_file != stdin && *cur_file)
461
      my_fclose(*cur_file,MYF(0));
462 463
  }
  DBUG_VOID_RETURN;
464 465
}

466

467 468 469 470
static void free_used_memory()
{
  uint i;
  DBUG_ENTER("free_used_memory");
471
#ifndef EMBEDDED_LIBRARY
472 473
  if (manager)
    mysql_manager_close(manager);
474
#endif
475 476
  close_cons();
  close_files();
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
477
  hash_free(&var_hash);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
478

479 480 481
  for (i=0 ; i < q_lines.elements ; i++)
  {
    struct st_query **q= dynamic_element(&q_lines, i, struct st_query**);
482
    my_free((gptr) (*q)->query_buf,MYF(MY_ALLOW_ZERO_PTR));
483 484
    my_free((gptr) (*q),MYF(0));
  }
485
  for (i=0; i < 10; i++)
486 487 488 489
  {
    if (var_reg[i].alloced_len)
      my_free(var_reg[i].str_val, MYF(MY_WME));
  }
490 491
  while (embedded_server_arg_count > 1)
    my_free(embedded_server_args[--embedded_server_arg_count],MYF(0));
492 493
  delete_dynamic(&q_lines);
  dynstr_free(&ds_res);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
494
  free_replace();
495
  free_replace_column();
496 497
  my_free(pass,MYF(MY_ALLOW_ZERO_PTR));
  free_defaults(default_argv);
498
  mysql_server_end();
499 500
  my_end(MY_CHECK_ERROR);
  DBUG_VOID_RETURN;
501 502 503 504 505
}

static void die(const char* fmt, ...)
{
  va_list args;
506
  DBUG_ENTER("die");
507
  va_start(args, fmt);
508 509 510 511 512
  if (fmt)
  {
    fprintf(stderr, "%s: ", my_progname);
    vfprintf(stderr, fmt, args);
    fprintf(stderr, "\n");
513
    fflush(stderr);
514
  }
515
  va_end(args);
516
  free_used_memory();
517 518 519
  exit(1);
}

520 521
/* Note that we will get some memory leaks when calling this! */

522 523
static void abort_not_supported_test()
{
524
  DBUG_ENTER("abort_not_supported_test");
525 526 527
  fprintf(stderr, "This test is not supported by this installation\n");
  if (!silent)
    printf("skipped\n");
528
  free_used_memory();
529 530 531 532 533 534
  exit(2);
}

static void verbose_msg(const char* fmt, ...)
{
  va_list args;
535 536 537
  DBUG_ENTER("verbose_msg");
  if (!verbose)
    DBUG_VOID_RETURN;
538 539 540

  va_start(args, fmt);

541
  fprintf(stderr, "%s: At line %u: ", my_progname, start_lineno);
542 543 544
  vfprintf(stderr, fmt, args);
  fprintf(stderr, "\n");
  va_end(args);
545
  DBUG_VOID_RETURN;
546 547
}

548

549 550 551 552 553
void init_parser()
{
  parser.current_line = parser.read_lines = 0;
  memset(&var_reg,0, sizeof(var_reg));
}
554 555


556
int dyn_string_cmp(DYNAMIC_STRING* ds, const char* fname)
557 558
{
  MY_STAT stat_info;
559 560
  char *tmp, *res_ptr;
  char eval_file[FN_REFLEN];
561
  int res;
562
  uint res_len;
563
  int fd;
564
  DYNAMIC_STRING res_ds;
565 566
  DBUG_ENTER("dyn_string_cmp");

567 568 569 570 571 572 573 574 575
  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)))
576
    die(NullS);
577 578 579 580
  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));
581
    DBUG_RETURN(2);
582
  }
583
  if (!(tmp = (char*) my_malloc(stat_info.st_size + 1, MYF(MY_WME))))
584
    die(NullS);
585 586

  if ((fd = my_open(eval_file, O_RDONLY, MYF(MY_WME))) < 0)
587
    die(NullS);
588
  if (my_read(fd, (byte*)tmp, stat_info.st_size, MYF(MY_WME|MY_NABP)))
589
    die(NullS);
590 591 592 593 594 595
  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;
596
    if ((res_len = res_ds.length) != ds->length)
597 598 599 600 601 602 603 604 605 606
    {
      res = 2;
      goto err;
    }
  }
  else
  {
    res_ptr = tmp;
    res_len = stat_info.st_size;
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
607

608
  res = (memcmp(res_ptr, ds->str, res_len)) ?  1 : 0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
609

610 611 612 613
err:
  if (res && eval_result)
    str_to_file(fn_format(eval_file, fname, "", ".eval",2), res_ptr,
		res_len);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
614

615 616
  my_free((gptr) tmp, MYF(0));
  my_close(fd, MYF(MY_WME));
617
  dynstr_free(&res_ds);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
618

619
  DBUG_RETURN(res);
620 621
}

622
static int check_result(DYNAMIC_STRING* ds, const char* fname,
623
			my_bool require_option)
624 625
{
  int error = 0;
626 627 628 629
  int res=dyn_string_cmp(ds, fname);

  if (res && require_option)
    abort_not_supported_test();
630
  switch (res) {
631 632 633 634 635 636 637 638 639 640 641 642 643 644
  case 0:
    break; /* ok */
  case 2:
    verbose_msg("Result length mismatch");
    error = 1;
    break;
  case 1:
    verbose_msg("Result content mismatch");
    error = 1;
    break;
  default: /* impossible */
    die("Unknown error code from dyn_string_cmp()");
  }
  if (error)
645
    reject_dump(fname, ds->str, ds->length);
646 647 648
  return error;
}

649

650 651
VAR* var_get(const char* var_name, const char** var_name_end, my_bool raw,
	     my_bool ignore_not_existing)
652 653 654
{
  int digit;
  VAR* v;
655 656 657 658
  DBUG_ENTER("var_get");
  DBUG_PRINT("enter",("var_name: %s",var_name));

  if (*var_name != '$')
659
    goto err;
660
  digit = *++var_name - '0';
serg@serg.mylan's avatar
serg@serg.mylan committed
661
  if (digit < 0 || digit >= 10)
662
  {
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
663
    const char* save_var_name = var_name, *end;
664
    uint length;
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
665
    end = (var_name_end) ? *var_name_end : 0;
666
    while (my_isvar(charset_info,*var_name) && var_name != end)
667
      var_name++;
668 669 670 671
    if (var_name == save_var_name)
    {
      if (ignore_not_existing)
	DBUG_RETURN(0);
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
672
      die("Empty variable");
673
    }
674
    length= (uint) (var_name - save_var_name);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
675

676 677
    if (!(v = (VAR*) hash_search(&var_hash, save_var_name, length)) &&
        length < MAX_VAR_NAME)
678
    {
679
      char buff[MAX_VAR_NAME+1];
serg@serg.mylan's avatar
serg@serg.mylan committed
680
      strmake(buff, save_var_name, length);
681
      v= var_from_env(buff, "");
682
    }
683
    var_name--;					/* Point at last character */
684
  }
685
  else
686
    v = var_reg + digit;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
687

688 689 690 691
  if (!raw && v->int_dirty)
  {
    sprintf(v->str_val, "%d", v->int_val);
    v->int_dirty = 0;
692
    v->str_val_len = strlen(v->str_val);
693
  }
694
  if (var_name_end)
695
    *var_name_end = var_name  ;
696
  DBUG_RETURN(v);
697 698
err:
  if (var_name_end)
699 700
    *var_name_end = 0;
  die("Unsupported variable name: %s", var_name);
701
  DBUG_RETURN(0);
702 703
}

sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
704 705 706
static VAR* var_obtain(char* name, int len)
{
  VAR* v;
707
  if ((v = (VAR*)hash_search(&var_hash, name, len)))
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
708
    return v;
709
  v = var_init(0, name, len, "", 0);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
710
  my_hash_insert(&var_hash, (byte*)v);
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
711 712 713
  return v;
}

714 715 716 717 718
int var_set(char* var_name, char* var_name_end, char* var_val,
	    char* var_val_end)
{
  int digit;
  VAR* v;
719
  if (*var_name++ != '$')
720 721 722 723 724 725
    {
      --var_name;
      *var_name_end = 0;
      die("Variable name in %s does not start with '$'", var_name);
    }
  digit = *var_name - '0';
726
  if (!(digit < 10 && digit >= 0))
727
    {
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
728
      v = var_obtain(var_name, var_name_end - var_name);
729
    }
730
  else
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
731
   v = var_reg + digit;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
732

733
  return eval_expr(v, var_val, (const char**)&var_val_end);
734 735
}

736 737
int open_file(const char* name)
{
738 739 740 741 742 743 744 745
  char buff[FN_REFLEN];
  if (!test_if_hard_path(name))
  {
    strxmov(buff, opt_basedir, name, NullS);
    name=buff;
  }
  fn_format(buff,name,"","",4);

746
  if (*cur_file && cur_file == file_stack_end)
747
    die("Source directives are nesting too deep");
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
748
  if (!(*(cur_file+1) = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
749 750
    die(NullS);
  cur_file++;
751
  *++lineno=1;
752

753 754 755
  return 0;
}

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
756

757
/* ugly long name, but we are following the convention */
758
int do_wait_for_slave_to_stop(struct st_query* q __attribute__((unused)))
759 760 761 762
{
  MYSQL* mysql = &cur_con->mysql;
  for (;;)
  {
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
763
    MYSQL_RES *res;
764 765 766
    MYSQL_ROW row;
    int done;
    LINT_INIT(res);
767

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
768 769
    if (mysql_query(mysql,"show status like 'Slave_running'") ||
	!(res=mysql_store_result(mysql)))
770 771 772 773 774 775 776 777 778 779 780
      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;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
781
    my_sleep(SLAVE_POLL_INTERVAL);
782 783 784 785
  }
  return 0;
}

786
int do_require_manager(struct st_query* a __attribute__((unused)))
787 788 789 790 791 792
{
  if (!manager)
    abort_not_supported_test();
  return 0;
}

793
#ifndef EMBEDDED_LIBRARY
794 795 796 797 798 799 800 801 802 803 804 805 806 807
int do_server_start(struct st_query* q)
{
  return do_server_op(q,"start");
}

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

int do_server_op(struct st_query* q,const char* op)
{
  char* p=q->first_argument;
  char com_buf[256],*com_p;
808 809 810 811
  if (!manager)
  {
    die("Manager is not initialized, manager commands are not possible");
  }
812 813 814 815
  com_p=strmov(com_buf,op);
  com_p=strmov(com_p,"_exec ");
  if (!*p)
    die("Missing server name in server_%s\n",op);
816
  while (*p && !my_isspace(charset_info,*p))
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
817
   *com_p++= *p++;
818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
  *com_p++=' ';
  com_p=int10_to_str(manager_wait_timeout,com_p,10);
  *com_p++ = '\n';
  *com_p=0;
  if (mysql_manager_command(manager,com_buf,(int)(com_p-com_buf)))
    die("Error in command: %s(%d)",manager->last_error,manager->last_errno);
  while (!manager->eof)
  {
    if (mysql_manager_fetch_line(manager,com_buf,sizeof(com_buf)))
      die("Error fetching result line: %s(%d)", manager->last_error,
	  manager->last_errno);
  }

  return 0;
}
833
#endif
834

835
int do_source(struct st_query* q)
836
{
837
  char* p=q->first_argument, *name;
838
  if (!*p)
839 840
    die("Missing file name in source\n");
  name = p;
841
  while (*p && !my_isspace(charset_info,*p))
842 843
    p++;
  *p = 0;
844

845 846 847
  return open_file(name);
}

848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
/*
  Execute given command.

  SYNOPSIS
    do_exec()
    q	called command

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

  RETURN VALUES
    0	ok
    1	error
*/

kent@mysql.com's avatar
kent@mysql.com committed
864
static void do_exec(struct st_query* q)
865
{
kent@mysql.com's avatar
kent@mysql.com committed
866
  int error;
kent@mysql.com's avatar
kent@mysql.com committed
867
  DYNAMIC_STRING *ds= NULL;			/* Assign just to avoid warning */
868 869 870 871
  DYNAMIC_STRING ds_tmp;
  char buf[1024];
  FILE *res_file;
  char *cmd= q->first_argument;
monty@mysql.com's avatar
monty@mysql.com committed
872
  DBUG_ENTER("do_exec");
873

monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
874
  while (*cmd && my_isspace(charset_info, *cmd))
875 876 877 878
    cmd++;
  if (!*cmd)
    die("Missing argument in exec\n");

monty@mysql.com's avatar
monty@mysql.com committed
879 880 881 882 883
  DBUG_PRINT("info", ("Executing '%s'", cmd));

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

884
  if (disable_result_log)
885
  {
monty@mysql.com's avatar
monty@mysql.com committed
886
    while (fgets(buf, sizeof(buf), res_file))
887 888 889 890
    {
      buf[strlen(buf)-1]=0;
      DBUG_PRINT("exec_result",("%s", buf));
    }
891 892
  }
  else
893 894 895 896 897 898 899 900
  {
    if (q->record_file[0])
    {
      init_dynamic_string(&ds_tmp, "", 16384, 65536);
      ds= &ds_tmp;
    }
    else
      ds= &ds_res;
901

902 903
    while (fgets(buf, sizeof(buf), res_file))
      replace_dynstr_append_mem(ds, buf, strlen(buf));
kent@mysql.com's avatar
kent@mysql.com committed
904 905 906 907 908
  }

  error= pclose(res_file);

  if (error != 0)
kent@mysql.com's avatar
kent@mysql.com committed
909
    die("command \"%s\" failed", cmd);
monty@mysql.com's avatar
monty@mysql.com committed
910

kent@mysql.com's avatar
kent@mysql.com committed
911 912
  if (!disable_result_log)
  {
913 914
    if (glob_replace)
      free_replace();
915

916 917 918 919 920 921 922 923 924 925 926 927 928
    if (record)
    {
      if (!q->record_file[0] && !result_file)
        die("At line %u: Missing result file", start_lineno);
      if (!result_file)
        str_to_file(q->record_file, ds->str, ds->length);
    }
    else if (q->record_file[0])
    {
      error= check_result(ds, q->record_file, q->require_file);
    }
    if (ds == &ds_tmp)
      dynstr_free(&ds_tmp);
929 930 931
  }
}

monty@mysql.com's avatar
monty@mysql.com committed
932

933 934 935 936 937 938 939
int var_query_set(VAR* v, const char* p, const char** p_end)
{
  char* end = (char*)((p_end && *p_end) ? *p_end : p + strlen(p));
  MYSQL_RES *res;
  MYSQL_ROW row;
  MYSQL* mysql = &cur_con->mysql;
  LINT_INIT(res);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
940

941 942 943 944 945 946 947 948 949 950 951 952 953 954
  while (end > p && *end != '`')
    --end;
  if (p == end)
    die("Syntax error in query, missing '`'");
  ++p;

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

  if ((row = mysql_fetch_row(res)) && row[0])
955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976
  {
    /*
      Concatenate all row results with tab in between to allow us to work
      with results from many columns (for example from SHOW VARIABLES)
    */
    DYNAMIC_STRING result;
    uint i;
    ulong *lengths;
    char *end;

    init_dynamic_string(&result, "", 16384, 65536);
    lengths= mysql_fetch_lengths(res);
    for (i=0; i < mysql_num_fields(res); i++)
    {
      if (row[0])
	dynstr_append_mem(&result, row[i], lengths[i]);
      dynstr_append_mem(&result, "\t", 1);
    }
    end= result.str + result.length-1;
    eval_expr(v, result.str, (const char**) &end);
    dynstr_free(&result);
  }
977 978 979 980 981 982
  else
    eval_expr(v, "", 0);

  mysql_free_result(res);
  return 0;
}
983

984 985 986 987 988
void var_copy(VAR* dest, VAR* src)
{
  dest->int_val=src->int_val;
  dest->int_dirty=src->int_dirty;
  if (dest->alloced_len < src->alloced_len &&
989
      !(dest->str_val=my_realloc(dest->str_val,src->alloced_len+1,
990 991 992
				 MYF(MY_WME))))
    die("Out of memory");
  dest->str_val_len=src->str_val_len;
993
  memcpy(dest->str_val,src->str_val,src->str_val_len+1);
994 995
}

996
int eval_expr(VAR* v, const char* p, const char** p_end)
997 998
{
  VAR* vp;
999
  if (*p == '$')
1000 1001
  {
    if ((vp = var_get(p,p_end,0,0)))
1002
    {
1003 1004
      var_copy(v, vp);
      return 0;
1005
    }
1006
  }
1007
  else if (*p == '`')
1008 1009 1010
  {
    return var_query_set(v, p, p_end);
  }
1011 1012
  else
    {
1013 1014 1015 1016
      int new_val_len = (p_end && *p_end) ?
	 (int) (*p_end - p) : (int) strlen(p);
      if (new_val_len + 1 >= v->alloced_len)
      {
1017
	v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
1018 1019
	  MIN_VAR_ALLOC : new_val_len + 1;
	if (!(v->str_val =
1020
	      v->str_val ? my_realloc(v->str_val, v->alloced_len+1,
1021
				      MYF(MY_WME)) :
1022
	      my_malloc(v->alloced_len+1, MYF(MY_WME))))
1023 1024 1025 1026 1027
	  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;
1028 1029
      v->int_val=atoi(p);
      v->int_dirty=0;
1030 1031
      return 0;
    }
1032

1033 1034 1035 1036
  die("Invalid expr: %s", p);
  return 1;
}

1037
int do_inc(struct st_query* q)
1038
{
1039
  char* p=q->first_argument;
1040
  VAR* v;
1041
  v = var_get(p, 0, 1, 0);
1042 1043 1044 1045 1046
  v->int_val++;
  v->int_dirty = 1;
  return 0;
}

1047
int do_dec(struct st_query* q)
1048
{
1049
  char* p=q->first_argument;
1050
  VAR* v;
1051
  v = var_get(p, 0, 1, 0);
1052 1053 1054 1055 1056
  v->int_val--;
  v->int_dirty = 1;
  return 0;
}

1057
int do_system(struct st_query* q)
1058
{
1059
  char* p=q->first_argument;
1060
  VAR v;
1061
  var_init(&v, 0, 0, 0, 0);
1062
  eval_expr(&v, p, 0); /* NULL terminated */
1063
  if (v.str_val_len)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1064
  {
monty@mysql.com's avatar
monty@mysql.com committed
1065
    char expr_buf[1024];
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1066 1067 1068 1069 1070 1071 1072 1073
    if ((uint)v.str_val_len > sizeof(expr_buf) - 1)
      v.str_val_len = sizeof(expr_buf) - 1;
    memcpy(expr_buf, v.str_val, v.str_val_len);
    expr_buf[v.str_val_len] = 0;
    DBUG_PRINT("info", ("running system command '%s'", expr_buf));
    if (system(expr_buf) && q->abort_on_error)
      die("system command '%s' failed", expr_buf);
  }
1074
  var_free(&v);
1075 1076
  return 0;
}
1077

1078
int do_echo(struct st_query* q)
1079
{
1080
  char* p=q->first_argument;
1081
  VAR v;
1082
  var_init(&v,0,0,0,0);
1083
  eval_expr(&v, p, 0); /* NULL terminated */
1084 1085 1086 1087 1088
  if (v.str_val_len)
  {
    fflush(stdout);
    write(1, v.str_val, v.str_val_len);
  }
1089
  write(1, "\n", 1);
1090
  var_free(&v);
1091 1092 1093
  return 0;
}

1094

1095
int do_sync_with_master2(const char* p)
1096 1097 1098 1099 1100
{
  MYSQL_RES* res;
  MYSQL_ROW row;
  MYSQL* mysql = &cur_con->mysql;
  char query_buf[FN_REFLEN+128];
1101
  int offset= 0, tries= 0;
1102 1103
  int rpl_parse;

1104 1105 1106 1107
  if (!master_pos.file[0])
  {
    die("Line %u: Calling 'sync_with_master' without calling 'save_master_pos'", start_lineno);
  }
1108 1109
  rpl_parse = mysql_rpl_parse_enabled(mysql);
  mysql_disable_rpl_parse(mysql);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1110

1111
  if (*p)
1112
    offset = atoi(p);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1113

1114
  sprintf(query_buf, "select master_pos_wait('%s', %ld)", master_pos.file,
1115
	  master_pos.pos + offset);
1116 1117 1118

wait_for_position:

1119 1120
  if (mysql_query(mysql, query_buf))
    die("line %u: failed in %s: %d: %s", start_lineno, query_buf,
1121 1122
	mysql_errno(mysql), mysql_error(mysql));

1123
  if (!(last_result = res = mysql_store_result(mysql)))
1124 1125
    die("line %u: mysql_store_result() returned NULL for '%s'", start_lineno,
	query_buf);
1126
  if (!(row = mysql_fetch_row(res)))
1127
    die("line %u: empty result in %s", start_lineno, query_buf);
1128
  if (!row[0])
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
  {
    /*
      It may be that the slave SQL thread has not started yet, though START
      SLAVE has been issued ?
    */
    if (tries++ == 3)
    {
      die("line %u: could not sync with master ('%s' returned NULL)", 
          start_lineno, query_buf);
    }
    sleep(1); /* So at most we will wait 3 seconds and make 4 tries */
    mysql_free_result(res);
    goto wait_for_position;
  }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1143 1144
  mysql_free_result(res);
  last_result=0;
1145
  if (rpl_parse)
1146
    mysql_enable_rpl_parse(mysql);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1147

1148 1149 1150
  return 0;
}

1151 1152 1153 1154
int do_sync_with_master(struct st_query* q)
{
  return do_sync_with_master2(q->first_argument);
}
1155

1156 1157 1158 1159 1160
int do_save_master_pos()
{
  MYSQL_RES* res;
  MYSQL_ROW row;
  MYSQL* mysql = &cur_con->mysql;
1161
  const char *query;
1162 1163 1164 1165
  int rpl_parse;

  rpl_parse = mysql_rpl_parse_enabled(mysql);
  mysql_disable_rpl_parse(mysql);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1166

1167
  if (mysql_query(mysql, query= "show master status"))
1168 1169 1170
    die("At line %u: failed in show master status: %d: %s", start_lineno,
	mysql_errno(mysql), mysql_error(mysql));

1171
  if (!(last_result =res = mysql_store_result(mysql)))
1172 1173
    die("line %u: mysql_store_result() retuned NULL for '%s'", start_lineno,
	query);
1174
  if (!(row = mysql_fetch_row(res)))
1175
    die("line %u: empty result in show master status", start_lineno);
1176 1177
  strnmov(master_pos.file, row[0], sizeof(master_pos.file)-1);
  master_pos.pos = strtoul(row[1], (char**) 0, 10);
1178
  mysql_free_result(res); last_result=0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1179

1180
  if (rpl_parse)
1181
    mysql_enable_rpl_parse(mysql);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1182

1183 1184 1185 1186
  return 0;
}


1187
int do_let(struct st_query* q)
1188
{
1189 1190
  char* p=q->first_argument;
  char *var_name, *var_name_end, *var_val_start;
1191
  if (!*p)
1192 1193
    die("Missing variable name in let\n");
  var_name = p;
1194
  while (*p && (*p != '=' || my_isspace(charset_info,*p)))
1195 1196
    p++;
  var_name_end = p;
1197
  if (*p == '=') p++;
1198
  while (*p && my_isspace(charset_info,*p))
1199 1200
    p++;
  var_val_start = p;
1201
  return var_set(var_name, var_name_end, var_val_start, q->end);
1202 1203
}

1204
int do_rpl_probe(struct st_query* q __attribute__((unused)))
1205
{
1206
  DBUG_ENTER("do_rpl_probe");
1207
  if (mysql_rpl_probe(&cur_con->mysql))
1208 1209
    die("Failed in mysql_rpl_probe(): '%s'", mysql_error(&cur_con->mysql));
  DBUG_RETURN(0);
1210 1211
}

1212
int do_enable_rpl_parse(struct st_query* q __attribute__((unused)))
1213 1214 1215 1216 1217
{
  mysql_enable_rpl_parse(&cur_con->mysql);
  return 0;
}

1218
int do_disable_rpl_parse(struct st_query* q __attribute__((unused)))
1219 1220 1221 1222 1223 1224
{
  mysql_disable_rpl_parse(&cur_con->mysql);
  return 0;
}


1225
int do_sleep(struct st_query* q, my_bool real_sleep)
1226
{
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1227
  char *p=q->first_argument;
1228
  while (*p && my_isspace(charset_info,*p))
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1229
    p++;
1230 1231
  if (!*p)
    die("Missing argument in sleep\n");
1232
  if (opt_sleep && !real_sleep)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1233
    my_sleep(opt_sleep * 1000000L);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1234
  else
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1235
    my_sleep((ulong) (atof(p) * 1000000L));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1236
  return 0;
1237 1238
}

1239
static void get_file_name(char *filename, struct st_query* q)
1240
{
1241
  char* p=q->first_argument;
1242 1243
  strnmov(filename, p, FN_REFLEN);
  /* Remove end space */
1244
  while (p > filename && my_isspace(charset_info,p[-1]))
1245 1246 1247 1248
    p--;
  p[0]=0;
}

1249

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1250
static uint get_ints(uint *to,struct st_query* q)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1251 1252
{
  char* p=q->first_argument;
1253
  long val;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1254
  uint count=0;
1255 1256
  DBUG_ENTER("get_ints");

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1257 1258
  if (!*p)
    die("Missing argument in %s\n", q->query);
1259 1260 1261

  for (; (p=str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val)) ; p++)
  {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1262
    count++;
1263 1264 1265 1266 1267
    *to++= (uint) val;
    if (*p != ',')
      break;
  }
  *to++=0;					/* End of data */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1268
  DBUG_RETURN(count);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1269 1270
}

1271 1272 1273
/*
  Get a string;  Return ptr to end of string
  Strings may be surrounded by " or '
1274 1275

  If string is a '$variable', return the value of the variable.
1276 1277 1278
*/


1279 1280
static char *get_string(char **to_ptr, char **from_ptr,
			struct st_query* q)
1281 1282
{
  reg1 char c,sep;
1283
  char *to= *to_ptr, *from= *from_ptr, *start=to;
1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329
  DBUG_ENTER("get_string");

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

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

1330
  while (my_isspace(charset_info,*from))	/* Point to next string */
1331 1332
    from++;

1333 1334
  *to =0;				/* End of string marker */
  *to_ptr= to+1;			/* Store pointer to end */
1335
  *from_ptr= from;
1336 1337 1338 1339 1340 1341 1342 1343

  /* 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)
    {
1344
      DBUG_PRINT("info",("var: '%s' -> '%s'", start, var->str_val));
1345 1346 1347 1348
      DBUG_RETURN(var->str_val);	/* return found variable value */
    }
  }
  DBUG_RETURN(start);
1349 1350 1351 1352 1353 1354 1355
}


/*
  Get arguments for replace. The syntax is:
  replace from to [from to ...]
  Where each argument may be quoted with ' or "
1356 1357
  A argument may also be a variable, in which case the value of the
  variable is replaced.
1358 1359 1360 1361 1362 1363
*/

static void get_replace(struct st_query *q)
{
  uint i;
  char *from=q->first_argument;
1364
  char *buff,*start;
1365 1366 1367 1368
  char word_end_chars[256],*pos;
  POINTER_ARRAY to_array,from_array;
  DBUG_ENTER("get_replace");

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1369
  free_replace();
1370

1371 1372 1373 1374
  bzero((char*) &to_array,sizeof(to_array));
  bzero((char*) &from_array,sizeof(from_array));
  if (!*from)
    die("Missing argument in %s\n", q->query);
1375
  start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
1376 1377 1378
  while (*from)
  {
    char *to=buff;
1379
    to=get_string(&buff, &from, q);
1380
    if (!*from)
1381
      die("Wrong number of arguments to replace in %s\n", q->query);
1382
    insert_pointer_name(&from_array,to);
1383
    to=get_string(&buff, &from, q);
1384 1385 1386
    insert_pointer_name(&to_array,to);
  }
  for (i=1,pos=word_end_chars ; i < 256 ; i++)
1387
    if (my_isspace(charset_info,i))
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1388
      *pos++= i;
1389
  *pos=0;					/* End pointer */
1390 1391 1392 1393 1394 1395 1396 1397
  if (!(glob_replace=init_replace((char**) from_array.typelib.type_names,
				  (char**) to_array.typelib.type_names,
				  (uint) from_array.typelib.count,
				  word_end_chars)) ||
      initialize_replace_buffer())
    die("Can't initialize replace from %s\n", q->query);
  free_pointer_array(&from_array);
  free_pointer_array(&to_array);
1398
  my_free(start, MYF(0));
1399
  DBUG_VOID_RETURN;
1400 1401 1402 1403
}

void free_replace()
{
1404
  DBUG_ENTER("free_replace");
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1405 1406 1407 1408 1409 1410
  if (glob_replace)
  {
    my_free((char*) glob_replace,MYF(0));
    glob_replace=0;
    free_replace_buffer();
  }
1411
  DBUG_VOID_RETURN;
1412 1413
}

1414
int select_connection(char *p)
1415
{
1416
  char* name;
1417
  struct connection *con;
1418 1419 1420
  DBUG_ENTER("select_connection");
  DBUG_PRINT("enter",("name: '%s'",p));

1421
  if (!*p)
1422 1423
    die("Missing connection name in connect\n");
  name = p;
1424
  while (*p && !my_isspace(charset_info,*p))
1425 1426
    p++;
  *p = 0;
1427

1428 1429
  for (con = cons; con < next_con; con++)
  {
1430
    if (!strcmp(con->name, name))
1431 1432 1433 1434 1435
    {
      cur_con = con;
      DBUG_RETURN(0);
    }
  }
1436
  die("connection '%s' not found in connection pool", name);
1437
  DBUG_RETURN(1);				/* Never reached */
1438 1439
}

1440
int close_connection(struct st_query* q)
1441
{
1442
  char* p=q->first_argument, *name;
1443
  struct connection *con;
1444 1445 1446
  DBUG_ENTER("close_connection");
  DBUG_PRINT("enter",("name: '%s'",p));

1447
  if (!*p)
1448 1449
    die("Missing connection name in connect\n");
  name = p;
1450
  while (*p && !my_isspace(charset_info,*p))
1451 1452
    p++;
  *p = 0;
1453

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1454
  for (con = cons; con < next_con; con++)
1455
  {
1456
    if (!strcmp(con->name, name))
1457
    {
1458
#ifndef EMBEDDED_LIBRARY
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1459 1460 1461
      if (q->type == Q_DIRTY_CLOSE)
      {
	if (con->mysql.net.vio)
1462
	{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1463 1464
	  vio_delete(con->mysql.net.vio);
	  con->mysql.net.vio = 0;
1465
	}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1466
      }
1467
#endif
1468 1469 1470 1471
      mysql_close(&con->mysql);
      DBUG_RETURN(0);
    }
  }
1472
  die("connection '%s' not found in connection pool", name);
1473
  DBUG_RETURN(1);				/* Never reached */
1474 1475
}

1476

1477 1478
/*
   This one now is a hack - we may want to improve in in the
1479 1480 1481
   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
1482
*/
1483

1484 1485
char* safe_get_param(char* str, char** arg, const char* msg)
{
1486
  DBUG_ENTER("safe_get_param");
1487
  while (*str && my_isspace(charset_info,*str))
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1488
    str++;
1489
  *arg = str;
1490 1491
  for (; *str && *str != ',' && *str != ')' ; str++)
  {
1492
    if (my_isspace(charset_info,*str))
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1493
      *str = 0;
1494
  }
1495
  if (!*str)
1496
    die(msg);
1497

1498
  *str++ = 0;
1499
  DBUG_RETURN(str);
1500 1501
}

1502
#ifndef EMBEDDED_LIBRARY
1503 1504 1505 1506 1507 1508 1509 1510 1511 1512
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);

}
1513
#endif
1514

1515 1516 1517 1518 1519 1520 1521 1522
int safe_connect(MYSQL* con, const char* host, const char* user,
		 const char* pass,
		 const char* db, int port, const char* sock)
{
  int con_error = 1;
  int i;
  for (i = 0; i < MAX_CON_TRIES; ++i)
  {
1523 1524
    if (mysql_real_connect(con, host,user, pass, db, port, sock,
			   CLIENT_MULTI_STATEMENTS))
1525 1526 1527 1528 1529 1530 1531 1532 1533
    {
      con_error = 0;
      break;
    }
    sleep(CON_RETRY_SLEEP);
  }
  return con_error;
}

1534 1535

int do_connect(struct st_query* q)
1536 1537 1538
{
  char* con_name, *con_user,*con_pass, *con_host, *con_port_str,
    *con_db, *con_sock;
1539
  char* p=q->first_argument;
1540
  char buff[FN_REFLEN];
1541
  int con_port;
1542
  int free_con_sock = 0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1543

1544 1545
  DBUG_ENTER("do_connect");
  DBUG_PRINT("enter",("connect: %s",p));
1546

1547
  if (*p != '(')
1548
    die("Syntax error in connect - expected '(' found '%c'", *p);
1549 1550 1551 1552 1553 1554
  p++;
  p = safe_get_param(p, &con_name, "missing connection name");
  p = safe_get_param(p, &con_host, "missing connection host");
  p = safe_get_param(p, &con_user, "missing connection user");
  p = safe_get_param(p, &con_pass, "missing connection password");
  p = safe_get_param(p, &con_db, "missing connection db");
1555 1556 1557 1558 1559 1560 1561
  if (!*p || *p == ';')				/* Default port and sock */
  {
    con_port=port;
    con_sock=(char*) unix_sock;
  }
  else
  {
1562
    VAR* var_port, *var_sock;
1563
    p = safe_get_param(p, &con_port_str, "missing connection port");
1564 1565
    if (*con_port_str == '$')
    {
1566
      if (!(var_port = var_get(con_port_str, 0, 0, 0)))
1567 1568 1569 1570
	die("Unknown variable '%s'", con_port_str+1);
      con_port = var_port->int_val;
    }
    else
1571
      con_port=atoi(con_port_str);
1572
    p = safe_get_param(p, &con_sock, "missing connection socket");
1573 1574
    if (*con_sock == '$')
    {
1575
      if (!(var_sock = var_get(con_sock, 0, 0, 0)))
1576 1577 1578 1579 1580 1581 1582
	die("Unknown variable '%s'", con_sock+1);
      if (!(con_sock = (char*)my_malloc(var_sock->str_val_len+1, MYF(0))))
	die("Out of memory");
      free_con_sock = 1;
      memcpy(con_sock, var_sock->str_val, var_sock->str_val_len);
      con_sock[var_sock->str_val_len] = 0;
    }
1583
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1584

1585
  if (next_con == cons_end)
jcole@tetra.spaceapes.com's avatar
jcole@tetra.spaceapes.com committed
1586
    die("Connection limit exhausted - increase MAX_CONS in mysqltest.c");
1587

1588
  if (!mysql_init(&next_con->mysql))
1589
    die("Failed on mysql_init()");
1590 1591
  if (opt_compress)
    mysql_options(&next_con->mysql,MYSQL_OPT_COMPRESS,NullS);
1592
  mysql_options(&next_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
bar@mysql.com's avatar
bar@mysql.com committed
1593
  mysql_options(&next_con->mysql, MYSQL_SET_CHARSET_NAME, "latin1");
1594

gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1595 1596 1597 1598 1599
#ifdef HAVE_OPENSSL
  if (opt_use_ssl)
    mysql_ssl_set(&next_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
		  opt_ssl_capath, opt_ssl_cipher);
#endif
1600
  if (con_sock && !free_con_sock && *con_sock && *con_sock != FN_LIBCHAR)
1601
    con_sock=fn_format(buff, con_sock, TMPDIR, "",0);
1602 1603
  if (!con_db[0])
    con_db=db;
1604
  /* Special database to allow one to connect without a database name */
1605
  if (con_db && !strcmp(con_db,"*NO-ONE*"))
1606
    con_db=0;
monty@mysql.com's avatar
monty@mysql.com committed
1607 1608 1609
  if ((safe_connect(&next_con->mysql, con_host,
		    con_user, con_pass,
		    con_db, con_port, con_sock ? con_sock: 0)))
1610 1611 1612
    die("Could not open connection '%s': %s", con_name,
	mysql_error(&next_con->mysql));

1613
  if (!(next_con->name = my_strdup(con_name, MYF(MY_WME))))
1614
    die(NullS);
1615
  cur_con = next_con++;
1616 1617
  if (free_con_sock)
    my_free(con_sock, MYF(MY_WME));
1618
  DBUG_RETURN(0);
1619 1620
}

1621

1622
int do_done(struct st_query* q)
1623 1624
{
  q->type = Q_END_BLOCK;
1625
  if (cur_block == block_stack)
1626
    die("Stray '}' - end of block before beginning");
1627 1628
  if (*block_ok--)
  {
1629
    parser.current_line = *--cur_block;
1630
  }
1631
  else
1632
  {
1633 1634
    ++parser.current_line;
    --cur_block;
1635
  }
1636 1637 1638
  return 0;
}

1639
int do_while(struct st_query* q)
1640
{
1641
  char* p=q->first_argument;
1642
  const char* expr_start, *expr_end;
1643
  VAR v;
1644
  if (cur_block == block_stack_end)
1645
    die("Nesting too deeply");
1646
  if (!*block_ok)
1647 1648 1649 1650 1651 1652
  {
    ++false_block_depth;
    *++block_ok = 0;
    *cur_block++ = parser.current_line++;
    return 0;
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1653

1654
  expr_start = strchr(p, '(');
1655
  if (!expr_start)
1656 1657
    die("missing '(' in while");
  expr_end = strrchr(expr_start, ')');
1658
  if (!expr_end)
1659
    die("missing ')' in while");
1660
  var_init(&v,0,0,0,0);
1661
  eval_expr(&v, ++expr_start, &expr_end);
1662
  *cur_block++ = parser.current_line++;
1663
  if (!v.int_val)
1664 1665 1666 1667
  {
    *++block_ok = 0;
    false_block_depth++;
  }
1668 1669
  else
    *++block_ok = 1;
1670
  var_free(&v);
1671
  return 0;
1672 1673
}

1674

1675 1676 1677 1678 1679 1680 1681 1682 1683 1684
/*
  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.
*/

1685
int my_getc(FILE *file)
1686 1687 1688
{
  if (line_buffer_pos == line_buffer)
    return fgetc(file);
1689
  return *--line_buffer_pos;
1690 1691 1692 1693
}

void my_ungetc(int c)
{
1694
  *line_buffer_pos++= (char) c;
1695 1696 1697 1698
}


my_bool end_of_query(int c)
1699
{
1700
  uint i;
1701
  char tmp[MAX_DELIMITER];
1702

1703 1704 1705 1706 1707 1708
  if (c != *delimiter)
    return 0;

  for (i= 1; i < delimiter_length &&
	 (c= my_getc(*cur_file)) == *(delimiter + i);
       i++)
1709 1710 1711
    tmp[i]= c;

  if (i == delimiter_length)
1712 1713 1714
    return 1;					/* Found delimiter */

  /* didn't find delimiter, push back things that we read */
1715 1716 1717
  my_ungetc(c);
  while (i > 1)
    my_ungetc(tmp[--i]);
1718 1719 1720 1721
  return 0;
}


1722 1723 1724
int read_line(char* buf, int size)
{
  int c;
1725 1726
  char* p= buf, *buf_end= buf + size - 1;
  int no_save= 0;
1727 1728
  enum {R_NORMAL, R_Q1, R_ESC_Q_Q1, R_ESC_Q_Q2,
	R_ESC_SLASH_Q1, R_ESC_SLASH_Q2,
1729
	R_Q2, R_COMMENT, R_LINE_START} state= R_LINE_START;
monty@mysql.com's avatar
monty@mysql.com committed
1730
  DBUG_ENTER("read_line");
1731

1732
  start_lineno= *lineno;
1733 1734
  for (; p < buf_end ;)
  {
1735
    no_save= 0;
1736
    c= my_getc(*cur_file);
1737
    if (feof(*cur_file))
1738
    {
1739
      if ((*cur_file) != stdin)
1740
	my_fclose(*cur_file, MYF(0));
1741 1742
      cur_file--;
      lineno--;
1743
      if (cur_file == file_stack)
monty@mysql.com's avatar
monty@mysql.com committed
1744
	DBUG_RETURN(1);
1745
      continue;
1746
    }
1747

1748 1749
    switch(state) {
    case R_NORMAL:
1750
      /*  Only accept '{' in the beginning of a line */
1751 1752 1753
      if (end_of_query(c))
      {
	*p= 0;
monty@mysql.com's avatar
monty@mysql.com committed
1754
	DBUG_RETURN(0);
1755
      }
1756 1757 1758 1759 1760
      else if (c == '\'')
	state = R_Q1;
      else if (c == '"')
	state = R_Q2;
      else if (c == '\n')
1761
      {
1762
	state = R_LINE_START;
1763 1764
	(*lineno)++;
      }
1765 1766 1767 1768
      break;
    case R_COMMENT:
      if (c == '\n')
      {
1769
	*p= 0;
1770
	(*lineno)++;
monty@mysql.com's avatar
monty@mysql.com committed
1771
	DBUG_RETURN(0);
1772 1773 1774 1775 1776 1777 1778
      }
      break;
    case R_LINE_START:
      if (c == '#' || c == '-')
      {
	state = R_COMMENT;
      }
1779
      else if (my_isspace(charset_info, c))
1780 1781 1782
      {
	if (c == '\n')
	  start_lineno= ++*lineno;		/* Query hasn't started yet */
1783
	no_save= 1;
1784
      }
1785 1786
      else if (c == '}')
      {
1787 1788
	*buf++= '}';
	*buf= 0;
monty@mysql.com's avatar
monty@mysql.com committed
1789
	DBUG_RETURN(0);
1790
      }
1791
      else if (end_of_query(c) || c == '{')
1792
      {
1793
	*p= 0;
monty@mysql.com's avatar
monty@mysql.com committed
1794
	DBUG_RETURN(0);
1795
      }
1796
      else if (c == '\'')
1797
	state= R_Q1;
1798
      else if (c == '"')
1799
	state= R_Q2;
1800
      else
1801
	state= R_NORMAL;
1802
      break;
1803

1804 1805
    case R_Q1:
      if (c == '\'')
1806
	state= R_ESC_Q_Q1;
1807
      else if (c == '\\')
1808
	state= R_ESC_SLASH_Q1;
1809 1810
      break;
    case R_ESC_Q_Q1:
1811 1812 1813
      if (end_of_query(c))
      {
	*p= 0;
monty@mysql.com's avatar
monty@mysql.com committed
1814
	DBUG_RETURN(0);
1815
      }
1816
      if (c != '\'')
1817
	state= R_NORMAL;
1818
      else
1819
	state= R_Q1;
1820 1821
      break;
    case R_ESC_SLASH_Q1:
1822
      state= R_Q1;
1823
      break;
1824

1825 1826
    case R_Q2:
      if (c == '"')
1827
	state= R_ESC_Q_Q2;
1828
      else if (c == '\\')
1829
	state= R_ESC_SLASH_Q2;
1830 1831
      break;
    case R_ESC_Q_Q2:
1832 1833 1834
      if (end_of_query(c))
      {
	*p= 0;
monty@mysql.com's avatar
monty@mysql.com committed
1835
	DBUG_RETURN(0);
1836
      }
1837
      if (c != '"')
1838
	state= R_NORMAL;
1839
      else
1840
	state= R_Q2;
1841 1842
      break;
    case R_ESC_SLASH_Q2:
1843
      state= R_Q2;
1844
      break;
1845
    }
1846 1847

    if (!no_save)
1848
      *p++= c;
1849
  }
1850
  *p= 0;					/* Always end with \0 */
monty@mysql.com's avatar
monty@mysql.com committed
1851
  DBUG_RETURN(feof(*cur_file));
1852 1853
}

monty@mysql.com's avatar
monty@mysql.com committed
1854

1855 1856
static char read_query_buf[MAX_QUERY];

1857
int read_query(struct st_query** q_ptr)
1858
{
1859
  char *p = read_query_buf, * p1 ;
1860
  int expected_errno;
1861
  struct st_query* q;
monty@mysql.com's avatar
monty@mysql.com committed
1862
  DBUG_ENTER("read_query");
1863

1864 1865
  if (parser.current_line < parser.read_lines)
  {
1866
    get_dynamic(&q_lines, (gptr) q_ptr, parser.current_line) ;
monty@mysql.com's avatar
monty@mysql.com committed
1867
    DBUG_RETURN(0);
1868
  }
1869
  if (!(*q_ptr= q= (struct st_query*) my_malloc(sizeof(*q), MYF(MY_WME))) ||
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1870 1871
      insert_dynamic(&q_lines, (gptr) &q))
    die(NullS);
1872

1873 1874 1875
  q->record_file[0]= 0;
  q->require_file= 0;
  q->first_word_len= 0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1876

1877
  q->type = Q_UNKNOWN;
1878
  q->query_buf= q->query= 0;
1879
  if (read_line(read_query_buf, sizeof(read_query_buf)))
monty@mysql.com's avatar
monty@mysql.com committed
1880 1881
  {
    DBUG_PRINT("warning",("too long query"));
monty@mysql.com's avatar
monty@mysql.com committed
1882
    DBUG_RETURN(1);
monty@mysql.com's avatar
monty@mysql.com committed
1883 1884
  }
   DBUG_PRINT("info", ("query: %s", read_query_buf));
1885 1886 1887
  if (*p == '#')
  {
    q->type = Q_COMMENT;
1888 1889
    /* This goto is to avoid losing the "expected error" info. */
    goto end;
1890
  }
1891 1892
  memcpy((gptr) q->expected_errno, (gptr) global_expected_errno,
	 sizeof(global_expected_errno));
1893 1894 1895
  q->expected_errors= global_expected_errors;
  q->abort_on_error= global_expected_errors == 0;
  bzero((gptr) global_expected_errno, sizeof(global_expected_errno));
1896 1897
  global_expected_errors=0;
  if (p[0] == '-' && p[1] == '-')
1898
  {
1899 1900
    q->type= Q_COMMENT_WITH_COMMAND;
    p+= 2;					/* To calculate first word */
1901 1902 1903 1904
  }
  else
  {
    if (*p == '!')
1905
    {
1906
      q->abort_on_error= 0;
1907 1908 1909
      p++;
      if (*p == '$')
      {
1910
	expected_errno= 0;
1911
	p++;
1912
	for (; my_isdigit(charset_info, *p); p++)
1913
	  expected_errno = expected_errno * 10 + *p - '0';
1914 1915
	q->expected_errno[0] = expected_errno;
	q->expected_errno[1] = 0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1916
	q->expected_errors=1;
1917
      }
1918 1919
    }

1920
    while (*p && my_isspace(charset_info, *p))
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1921
      p++ ;
1922
    if (*p == '@')
1923 1924 1925
    {
      p++;
      p1 = q->record_file;
1926
      while (!my_isspace(charset_info, *p) &&
1927
	     p1 < q->record_file + sizeof(q->record_file) - 1)
1928
	*p1++ = *p++;
1929
      *p1 = 0;
1930
    }
1931
  }
1932 1933

end:
1934
  while (*p && my_isspace(charset_info, *p))
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1935
    p++;
1936
  if (!(q->query_buf= q->query= my_strdup(p, MYF(MY_WME))))
1937 1938 1939
    die(NullS);

  /* Calculate first word and first argument */
1940 1941 1942
  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))
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1943
    p++;
1944 1945
  q->first_argument= p;
  q->end= strend(q->query);
1946
  parser.read_lines++;
monty@mysql.com's avatar
monty@mysql.com committed
1947
  DBUG_RETURN(0);
1948 1949
}

1950 1951 1952

static struct my_option my_long_options[] =
{
1953
  {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
1954 1955 1956
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
  {"database", 'D', "Database to use.", (gptr*) &db, (gptr*) &db, 0,
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1957
  {"basedir", 'b', "Basedir for tests.", (gptr*) &opt_basedir,
1958
   (gptr*) &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1959
  {"big-test", 'B', "Define BIG_TEST to 1.", (gptr*) &opt_big_test,
1960
   (gptr*) &opt_big_test, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1961
  {"compress", 'C', "Use the compressed server/client protocol.",
1962 1963 1964 1965 1966 1967
   (gptr*) &opt_compress, (gptr*) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
   0, 0, 0},
  {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
   0, 0, 0, 0, 0, 0},
  {"host", 'h', "Connect to host.", (gptr*) &host, (gptr*) &host, 0,
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1968
  {"manager-user", OPT_MANAGER_USER, "Undocumented: Used for debugging.",
1969 1970
   (gptr*) &manager_user, (gptr*) &manager_user, 0, GET_STR, REQUIRED_ARG, 0,
   0, 0, 0, 0, 0},
1971
  {"manager-host", OPT_MANAGER_HOST, "Undocumented: Used for debugging.",
1972 1973
   (gptr*) &manager_host, (gptr*) &manager_host, 0, GET_STR, REQUIRED_ARG,
   0, 0, 0, 0, 0, 0},
1974
  {"manager-password", OPT_MANAGER_PASSWD, "Undocumented: Used for debugging.",
1975
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1976
  {"manager-port", OPT_MANAGER_PORT, "Undocumented: Used for debugging.",
1977 1978 1979
   (gptr*) &manager_port, (gptr*) &manager_port, 0, GET_INT, REQUIRED_ARG,
   MYSQL_MANAGER_PORT, 0, 0, 0, 0, 0},
  {"manager-wait-timeout", OPT_MANAGER_WAIT_TIMEOUT,
1980
   "Undocumented: Used for debugging.", (gptr*) &manager_wait_timeout,
1981 1982 1983 1984
   (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,
ulli@morbus.(none)'s avatar
ulli@morbus.(none) committed
1985
   (gptr*) &port, 0, GET_INT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0, 0},
1986 1987 1988 1989 1990 1991 1992
  {"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},
1993
  {"server-arg", 'A', "Send enbedded server this as a paramenter.",
1994
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1995
  {"server-file", 'F', "Read embedded server arguments from file.",
1996 1997 1998
   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},
1999
  {"skip-safemalloc", OPT_SKIP_SAFEMALLOC,
2000
   "Don't use the memory allocation checking.", 0, 0, 0, GET_NO_ARG, NO_ARG,
2001
   0, 0, 0, 0, 0, 0},
2002
  {"sleep", 'T', "Sleep always this many seconds on sleep commands.",
2003 2004 2005 2006 2007
   (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},
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2008
#include "sslopt-longopts.h"
2009 2010
  {"test-file", 'x', "Read test from/in this file (default stdin).",
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
kent@mysql.com's avatar
kent@mysql.com committed
2011 2012
  {"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},
2013
  {"tmpdir", 't', "Temporary directory where sockets are put.",
2014 2015 2016 2017 2018 2019 2020 2021
   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}
2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032
};

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();
2033
  printf("MySQL AB, by Sasha, Matt, Monty & Jani\n");
2034 2035 2036
  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);
2037
  my_print_help(my_long_options);
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
2038
  printf("  --no-defaults       Don't read default options from any options file.\n");
2039
  my_print_variables(my_long_options);
2040 2041
}

2042 2043 2044 2045 2046

static my_bool
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
	       char *argument)
{
2047
  switch(optid) {
2048
  case '#':
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2049
#ifndef DBUG_OFF
2050
    DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysqltest.trace");
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2051
#endif
2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069
    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);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2070
      if (!(*++cur_file = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
2071 2072 2073
	die("Could not open %s: errno = %d", argument, errno);
      break;
    }
kent@mysql.com's avatar
kent@mysql.com committed
2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086
  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;
    }
2087 2088 2089 2090 2091 2092
  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 */
2093
      tty_password= 0;
2094 2095 2096 2097
    }
    else
      tty_password= 1;
    break;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2098
#include <sslopt-case.h>
2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119
  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*) "";
    }
    embedded_server_args[embedded_server_arg_count++]=
      my_strdup(argument, MYF(MY_FAE));
    if (embedded_server_arg_count == MAX_SERVER_ARGS ||
	!embedded_server_args[embedded_server_arg_count-1])
    {
      die("Can't use server argument");
    }
    break;
  case 'F':
    if (read_server_arguments(argument))
      die(NullS);
    break;
2120 2121 2122 2123 2124
  case OPT_SKIP_SAFEMALLOC:
#ifdef SAFEMALLOC
    sf_malloc_quick=1;
#endif
    break;
2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135
  case 'V':
    print_version();
    exit(0);
  case '?':
    usage();
    exit(1);
  }
  return 0;
}


2136 2137 2138
int parse_args(int argc, char **argv)
{
  load_defaults("my",load_default_groups,&argc,&argv);
2139
  default_argv= argv;
2140

2141
  if ((handle_options(&argc, &argv, my_long_options, get_one_option)))
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2142
    exit(1);
2143 2144 2145 2146 2147 2148 2149

  if (argc > 1)
  {
    usage();
    exit(1);
  }
  if (argc == 1)
2150
    db= *argv;
2151 2152 2153 2154 2155 2156
  if (tty_password)
    pass=get_tty_password(NullS);

  return 0;
}

2157
char* safe_str_append(char* buf, const char* str, int size)
2158 2159
{
  int i,c ;
2160
  for (i = 0; (c = *str++) &&  i < size - 1; i++)
2161 2162 2163 2164 2165
    *buf++ = c;
  *buf = 0;
  return buf;
}

2166 2167 2168
void str_to_file(const char* fname, char* str, int size)
{
  int fd;
kent@mysql.com's avatar
kent@mysql.com committed
2169
  char buff[MAXPATHLEN];
2170 2171 2172 2173 2174 2175
  if (!test_if_hard_path(fname))
  {
    strxmov(buff, opt_basedir, fname, NullS);
    fname=buff;
  }
  fn_format(buff,fname,"","",4);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2176

2177
  if ((fd = my_open(buff, O_WRONLY | O_CREAT | O_TRUNC,
2178
		    MYF(MY_WME | MY_FFNF))) < 0)
2179
    die("Could not open %s: errno = %d", buff, errno);
2180
  if (my_write(fd, (byte*)str, size, MYF(MY_WME|MY_FNABP)))
2181 2182 2183 2184 2185
    die("write failed");
  my_close(fd, MYF(0));
}

void reject_dump(const char* record_file, char* buf, int size)
2186
{
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2187
  char reject_file[FN_REFLEN];
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2188
  str_to_file(fn_format(reject_file, record_file,"",".reject",2), buf, size);
2189 2190
}

2191 2192 2193

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

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2194 2195
static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
				      int len)
2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206
{
  if (glob_replace)
  {
    len=(int) replace_strings(glob_replace, &out_buff, &out_length, val);
    if (len == -1)
      die("Out of memory in replace\n");
    val=out_buff;
  }
  dynstr_append_mem(ds, val, len);
}

2207

2208 2209
/*
  Append all results to the dynamic string separated with '\t'
2210
  Values may be converted with 'replace_column'
2211 2212 2213 2214 2215
*/

static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
{
  MYSQL_ROW row;
2216
  uint num_fields= mysql_num_fields(res);
2217
  MYSQL_FIELD *fields= !display_result_vertically ? 0 : mysql_fetch_fields(res);
2218 2219 2220
  unsigned long *lengths;
  while ((row = mysql_fetch_row(res)))
  {
2221
    uint i;
2222 2223 2224 2225 2226
    lengths = mysql_fetch_lengths(res);
    for (i = 0; i < num_fields; i++)
    {
      const char *val= row[i];
      ulonglong len= lengths[i];
2227 2228 2229 2230 2231 2232

      if (i < max_replace_column && replace_column[i])
      {
	val= replace_column[i];
	len= strlen(val);
      }
2233 2234
      if (!val)
      {
2235 2236
	val= "NULL";
	len= 4;
2237
      }
2238 2239 2240 2241 2242 2243 2244 2245 2246
      if (!display_result_vertically)
      {
	if (i)
	  dynstr_append_mem(ds, "\t", 1);
	replace_dynstr_append_mem(ds, val, len);
      }
      else
      {
	dynstr_append(ds, fields[i].name);
2247
	dynstr_append_mem(ds, "\t", 1);
2248 2249 2250
	replace_dynstr_append_mem(ds, val, len);
	dynstr_append_mem(ds, "\n", 1);
      }
2251
    }
2252 2253
    if (!display_result_vertically)
      dynstr_append_mem(ds, "\n", 1);
2254
  }
2255
  free_replace_column();
2256 2257
}

2258

2259
/*
2260
* flags control the phased/stages of query execution to be performed
2261 2262 2263
* 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
*/
2264

2265
int run_query(MYSQL* mysql, struct st_query* q, int flags)
2266
{
2267
  MYSQL_RES* res= 0;
2268 2269
  uint i;
  int error= 0, err= 0, counter= 0;
2270 2271
  DYNAMIC_STRING *ds;
  DYNAMIC_STRING ds_tmp;
2272
  DYNAMIC_STRING eval_query;
2273
  char* query;
2274
  int query_len, got_error_on_send= 0;
2275
  DBUG_ENTER("run_query");
monty@mysql.com's avatar
monty@mysql.com committed
2276
  DBUG_PRINT("enter",("flags: %d", flags));
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2277
  
2278 2279 2280 2281 2282
  if (q->type != Q_EVAL)
  {
    query = q->query;
    query_len = strlen(query);
  }
2283
  else
2284 2285 2286 2287 2288 2289
  {
    init_dynamic_string(&eval_query, "", 16384, 65536);
    do_eval(&eval_query, q->query);
    query = eval_query.str;
    query_len = eval_query.length;
  }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2290
  DBUG_PRINT("enter", ("query: '%-.60s'", query));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2291

2292
  if (q->record_file[0])
2293
  {
2294
    init_dynamic_string(&ds_tmp, "", 16384, 65536);
2295 2296
    ds = &ds_tmp;
  }
2297 2298
  else
    ds= &ds_res;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2299

2300 2301 2302 2303 2304 2305 2306
  if (flags & QUERY_SEND)
  {
    got_error_on_send= mysql_send_query(mysql, query, query_len);
    if (got_error_on_send && !q->expected_errno[0])
      die("At line %u: unable to send query '%s' (mysql_errno=%d , errno=%d)",
	  start_lineno, query, mysql_errno(mysql), errno);
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2307

2308
  do
2309
  {
2310
    if ((flags & QUERY_SEND) && !disable_query_log && !counter)
2311
    {
2312
      replace_dynstr_append_mem(ds,query, query_len);
2313 2314
      dynstr_append_mem(ds, delimiter, delimiter_length);
      dynstr_append_mem(ds, "\n", 1);
2315
    }
2316 2317 2318
    if (!(flags & QUERY_REAP))
      DBUG_RETURN(0);

2319 2320 2321 2322
    if (got_error_on_send ||
	(!counter && (*mysql->methods->read_query_result)(mysql)) ||
	 (!(last_result= res= mysql_store_result(mysql)) &&
	  mysql_field_count(mysql)))
2323
    {
2324
      if (q->require_file)
2325
      {
2326 2327 2328 2329 2330 2331 2332 2333
	abort_not_supported_test();
      }
      if (q->abort_on_error)
	die("At line %u: query '%s' failed: %d: %s", start_lineno, query,
	    mysql_errno(mysql), mysql_error(mysql));
      else
      {
	for (i=0 ; (uint) i < q->expected_errors ; i++)
2334
	{
2335
	  if ((q->expected_errno[i] == mysql_errno(mysql)))
2336
	  {
2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351
	    if (i == 0 && q->expected_errors == 1)
	    {
	      /* Only log error if there is one possible error */
	      dynstr_append_mem(ds,"ERROR ",6);
	      replace_dynstr_append_mem(ds, mysql_sqlstate(mysql),
					strlen(mysql_sqlstate(mysql)));
	      dynstr_append_mem(ds, ": ", 2);
	      replace_dynstr_append_mem(ds,mysql_error(mysql),
					strlen(mysql_error(mysql)));
	      dynstr_append_mem(ds,"\n",1);
	    }
	    /* Don't log error if we may not get an error */
	    else if (q->expected_errno[0] != 0)
	      dynstr_append(ds,"Got one of the listed errors\n");
	    goto end;				/* Ok */
2352
	  }
2353
	}
monty@mysql.com's avatar
monty@mysql.com committed
2354 2355
	DBUG_PRINT("info",("i: %d  expected_errors: %d", i,
			   q->expected_errors));
2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375
	dynstr_append_mem(ds, "ERROR ",6);
	replace_dynstr_append_mem(ds, mysql_sqlstate(mysql),
				  strlen(mysql_sqlstate(mysql)));
	dynstr_append_mem(ds,": ",2);
	replace_dynstr_append_mem(ds, mysql_error(mysql),
				  strlen(mysql_error(mysql)));
	dynstr_append_mem(ds,"\n",1);
	if (i)
	{
	  verbose_msg("query '%s' failed with wrong errno %d instead of %d...",
		      q->query, mysql_errno(mysql), q->expected_errno[0]);
	  error= 1;
	  goto end;
	}
	verbose_msg("query '%s' failed: %d: %s", q->query, mysql_errno(mysql),
		    mysql_error(mysql));
	/*
	  if we do not abort on error, failure to run the query does
	  not fail the whole test case
	*/
2376 2377
	goto end;
      }
2378 2379 2380 2381 2382 2383
      /*{
	verbose_msg("failed in mysql_store_result for query '%s' (%d)", query,
	mysql_errno(mysql));
	error = 1;
	goto end;
	}*/
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2384
    }
2385 2386 2387

    if (q->expected_errno[0])
    {
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
2388
      error = 1;
2389 2390
      verbose_msg("query '%s' succeeded - should have failed with errno %d...",
		  q->query, q->expected_errno[0]);
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
2391
      goto end;
2392
    }
2393

2394
    if (!disable_result_log)
2395
    {
2396
      if (res)
2397
      {
2398 2399
	MYSQL_FIELD *field, *field_end;
	uint num_fields= mysql_num_fields(res);
2400

2401 2402
	if (display_metadata)
	{
2403
	  dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\tColumn_alias\tName\tType\tLength\tMax length\tIs_null\tFlags\tDecimals\tCharsetnr\n");
2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438
	  for (field= mysql_fetch_fields(res), field_end= field+num_fields ;
	       field < field_end ;
	       field++)
	  {
	    char buff[22];
	    dynstr_append_mem(ds, field->catalog, field->catalog_length);
	    dynstr_append_mem(ds, "\t", 1);
	    dynstr_append_mem(ds, field->db, field->db_length);
	    dynstr_append_mem(ds, "\t", 1);
	    dynstr_append_mem(ds, field->org_table, field->org_table_length);
	    dynstr_append_mem(ds, "\t", 1);
	    dynstr_append_mem(ds, field->table, field->table_length);
	    dynstr_append_mem(ds, "\t", 1);
	    dynstr_append_mem(ds, field->org_name, field->org_name_length);
	    dynstr_append_mem(ds, "\t", 1);
	    dynstr_append_mem(ds, field->name, field->name_length);
	    dynstr_append_mem(ds, "\t", 1);
	    int10_to_str((int) field->type, buff, 10);
	    dynstr_append(ds, buff);
	    dynstr_append_mem(ds, "\t", 1);
	    int10_to_str((int) field->length, buff, 10);
	    dynstr_append(ds, buff);
	    dynstr_append_mem(ds, "\t", 1);
	    int10_to_str((int) field->max_length, buff, 10);
	    dynstr_append(ds, buff);
	    dynstr_append_mem(ds, "\t", 1);
	    dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ?
					   "N" : "Y"), 1);
	    dynstr_append_mem(ds, "\t", 1);

	    int10_to_str((int) field->flags, buff, 10);
	    dynstr_append(ds, buff);
	    dynstr_append_mem(ds, "\t", 1);
	    int10_to_str((int) field->decimals, buff, 10);
	    dynstr_append(ds, buff);
2439 2440 2441
	    dynstr_append_mem(ds, "\t", 1);
	    int10_to_str((int) field->charsetnr, buff, 10);
	    dynstr_append(ds, buff);
2442 2443 2444
	    dynstr_append_mem(ds, "\n", 1);
	  }
	}
2445
	if (!display_result_vertically)
2446
	{
2447
	  field= mysql_fetch_fields(res);
2448 2449 2450 2451
	  for (i = 0; i < num_fields; i++)
	  {
	    if (i)
	      dynstr_append_mem(ds, "\t", 1);
2452 2453
	    replace_dynstr_append_mem(ds, field[i].name,
				      strlen(field[i].name));
2454 2455
	  }
	  dynstr_append_mem(ds, "\n", 1);
2456 2457
	}
	append_result(ds, res);
2458
      }
2459

2460 2461
      /* Add all warnings to the result */
      if (!disable_warnings && mysql_warning_count(mysql))
2462
      {
2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477
	MYSQL_RES *warn_res=0;
	uint count= mysql_warning_count(mysql);
	if (!mysql_real_query(mysql, "SHOW WARNINGS", 13))
	{
	  warn_res= mysql_store_result(mysql);
	}
	if (!warn_res)
	  verbose_msg("Warning count is %u but didn't get any warnings\n",
		      count);
	else
	{
	  dynstr_append_mem(ds, "Warnings:\n", 10);
	  append_result(ds, warn_res);
	  mysql_free_result(warn_res);
	}
2478
      }
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
2479
      if (!disable_info)
2480
      {
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
2481
	char buf[40];
2482
	sprintf(buf,"affected rows: %lu\n",(ulong) mysql_affected_rows(mysql));
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
2483 2484 2485 2486 2487 2488 2489
	dynstr_append(ds, buf);
	if (mysql_info(mysql))
	{
	  dynstr_append(ds, "info: ");
	  dynstr_append(ds, mysql_info(mysql));
	  dynstr_append_mem(ds, "\n", 1);
	}
2490
      }
2491
    }
2492

2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510
    if (record)
    {
      if (!q->record_file[0] && !result_file)
	die("At line %u: Missing result file", start_lineno);
      if (!result_file)
	str_to_file(q->record_file, ds->str, ds->length);
    }
    else if (q->record_file[0])
    {
      error = check_result(ds, q->record_file, q->require_file);
    }
    if (res)
      mysql_free_result(res);
    last_result= 0;
    counter++;
  } while (!(err= mysql_next_result(mysql)));
  if (err >= 1)
    mysql_error(mysql);
2511

2512
end:
2513
  free_replace();
2514
  last_result=0;
2515 2516
  if (ds == &ds_tmp)
    dynstr_free(&ds_tmp);
2517
  if (q->type == Q_EVAL)
2518
    dynstr_free(&eval_query);
2519
  DBUG_RETURN(error);
2520 2521 2522
}


2523
void get_query_type(struct st_query* q)
2524
{
2525 2526
  char save;
  uint type;
monty@mysql.com's avatar
monty@mysql.com committed
2527 2528
  DBUG_ENTER("get_query_type");

2529
  if (*q->query == '}')
2530 2531
  {
    q->type = Q_END_BLOCK;
monty@mysql.com's avatar
monty@mysql.com committed
2532
    DBUG_VOID_RETURN;
2533 2534 2535 2536
  }
  if (q->type != Q_COMMENT_WITH_COMMAND)
    q->type = Q_QUERY;

2537 2538
  save=q->query[q->first_word_len];
  q->query[q->first_word_len]=0;
2539
  type=find_type(q->query, &command_typelib, 1+2);
2540
  q->query[q->first_word_len]=save;
2541
  if (type > 0)
2542
    q->type=(enum enum_commands) type;		/* Found command */
monty@mysql.com's avatar
monty@mysql.com committed
2543
  DBUG_VOID_RETURN;
2544
}
2545

monty@mysql.com's avatar
monty@mysql.com committed
2546

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2547
static byte *get_var_key(const byte* var, uint* len,
2548
			 my_bool __attribute__((unused)) t)
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2549 2550 2551 2552 2553 2554 2555
{
  register char* key;
  key = ((VAR*)var)->name;
  *len = ((VAR*)var)->name_len;
  return (byte*)key;
}

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2556
static VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2557 2558 2559
		     int val_len)
{
  int val_alloc_len;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2560
  VAR *tmp_var;
2561
  if (!name_len && name)
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2562
    name_len = strlen(name);
2563
  if (!val_len && val)
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2564 2565
    val_len = strlen(val) ;
  val_alloc_len = val_len + 16; /* room to grow */
2566
  if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var)
2567
						 + name_len, MYF(MY_WME))))
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2568
    die("Out of memory");
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2569

2570
  tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0;
2571
  tmp_var->alloced = (v == 0);
2572

2573
  if (!(tmp_var->str_val = my_malloc(val_alloc_len+1, MYF(MY_WME))))
2574
    die("Out of memory");
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2575

sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2576
  memcpy(tmp_var->name, name, name_len);
2577 2578 2579 2580 2581
  if (val)
  {
    memcpy(tmp_var->str_val, val, val_len);
    tmp_var->str_val[val_len]=0;
  }
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2582 2583 2584
  tmp_var->name_len = name_len;
  tmp_var->str_val_len = val_len;
  tmp_var->alloced_len = val_alloc_len;
2585
  tmp_var->int_val = (val) ? atoi(val) : 0;
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2586 2587 2588 2589
  tmp_var->int_dirty = 0;
  return tmp_var;
}

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2590
static void var_free(void *v)
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2591
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2592
  my_free(((VAR*) v)->str_val, MYF(MY_WME));
2593 2594
  if (((VAR*)v)->alloced)
   my_free((char*) v, MYF(MY_WME));
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2595 2596 2597
}


2598
static VAR* var_from_env(const char *name, const char *def_val)
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2599
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2600 2601
  const char *tmp;
  VAR *v;
2602
  if (!(tmp = getenv(name)))
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2603
    tmp = def_val;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2604

2605
  v = var_init(0, name, 0, tmp, 0);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2606
  my_hash_insert(&var_hash, (byte*)v);
2607
  return v;
2608
}
2609

2610

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2611
static void init_var_hash(MYSQL *mysql)
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2612
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2613
  VAR *v;
2614
  DBUG_ENTER("init_var_hash");
2615
  if (hash_init(&var_hash, charset_info, 
2616
                1024, 0, 0, get_var_key, var_free, MYF(0)))
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2617
    die("Variable hash initialization failed");
2618 2619
  if (opt_big_test)
    my_hash_insert(&var_hash, (byte*) var_init(0,"BIG_TEST", 0, "1",0));
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2620
  v= var_init(0,"MAX_TABLES", 0, (sizeof(ulong) == 4) ? "31" : "62",0);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2621
  my_hash_insert(&var_hash, (byte*) v);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2622
  v= var_init(0,"SERVER_VERSION", 0, mysql_get_server_info(mysql), 0);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2623
  my_hash_insert(&var_hash, (byte*) v);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2624
  
2625
  DBUG_VOID_RETURN;
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2626
}
2627

2628

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2629
int main(int argc, char **argv)
2630 2631
{
  int error = 0;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2632
  struct st_query *q;
2633
  my_bool require_file=0, q_send_flag=0;
2634
  char save_file[FN_REFLEN];
2635
  MY_INIT(argv[0]);
2636
  {
2637 2638
  DBUG_ENTER("main");
  DBUG_PROCESS(argv[0]);
2639

kent@mysql.com's avatar
kent@mysql.com committed
2640 2641 2642
  /* Use all time until exit if no explicit 'start_timer' */
  timer_start= timer_now();

2643
  save_file[0]=0;
2644
  TMPDIR[0]=0;
2645 2646 2647 2648
  memset(cons, 0, sizeof(cons));
  cons_end = cons + MAX_CONS;
  next_con = cons + 1;
  cur_con = cons;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2649

2650
  memset(file_stack, 0, sizeof(file_stack));
2651
  memset(&master_pos, 0, sizeof(master_pos));
2652 2653
  file_stack_end = file_stack + MAX_INCLUDE_DEPTH;
  cur_file = file_stack;
2654
  lineno   = lineno_stack;
2655
  my_init_dynamic_array(&q_lines, sizeof(struct st_query*), INIT_Q_LINES,
2656 2657 2658
		     INIT_Q_LINES);
  memset(block_stack, 0, sizeof(block_stack));
  block_stack_end = block_stack + BLOCK_STACK_DEPTH;
2659
  memset(block_ok_stack, 0, sizeof(block_stack));
2660
  cur_block = block_stack;
2661 2662
  block_ok = block_ok_stack;
  *block_ok = 1;
2663
  init_dynamic_string(&ds_res, "", 0, 65536);
2664
  parse_args(argc, argv);
2665 2666 2667
  if (mysql_server_init(embedded_server_arg_count,
			embedded_server_args,
			(char**) embedded_server_groups))
2668 2669 2670
    die("Can't initialize MySQL server");
  if (cur_file == file_stack)
    *++cur_file = stdin;
2671
  *lineno=1;
2672 2673 2674
#ifndef EMBEDDED_LIBRARY
  if (manager_host)
    init_manager();
2675
#endif
2676
  if (!( mysql_init(&cur_con->mysql)))
2677
    die("Failed in mysql_init()");
2678 2679
  if (opt_compress)
    mysql_options(&cur_con->mysql,MYSQL_OPT_COMPRESS,NullS);
2680
  mysql_options(&cur_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
bar@mysql.com's avatar
bar@mysql.com committed
2681 2682
  mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, "latin1");

gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2683 2684 2685 2686 2687
#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
2688

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2689
  if (!(cur_con->name = my_strdup("default", MYF(MY_WME))))
2690
    die("Out of memory");
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2691

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2692
  if (safe_connect(&cur_con->mysql, host, user, pass, db, port, unix_sock))
2693 2694
    die("Failed in mysql_real_connect(): %s", mysql_error(&cur_con->mysql));

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2695 2696
  init_var_hash(&cur_con->mysql);

2697
  while (!read_query(&q))
2698 2699 2700 2701
  {
    int current_line_inc = 1, processed = 0;
    if (q->type == Q_UNKNOWN || q->type == Q_COMMENT_WITH_COMMAND)
      get_query_type(q);
2702
    if (*block_ok)
2703
    {
2704 2705 2706
      processed = 1;
      switch (q->type) {
      case Q_CONNECT: do_connect(q); break;
2707
      case Q_CONNECTION: select_connection(q->first_argument); break;
2708
      case Q_DISCONNECT:
2709
      case Q_DIRTY_CLOSE:
2710
	close_connection(q); break;
2711
      case Q_RPL_PROBE: do_rpl_probe(q); break;
2712
      case Q_ENABLE_RPL_PARSE:	 do_enable_rpl_parse(q); break;
2713
      case Q_DISABLE_RPL_PARSE:  do_disable_rpl_parse(q); break;
2714
      case Q_ENABLE_QUERY_LOG:	 disable_query_log=0; break;
2715 2716 2717
      case Q_DISABLE_QUERY_LOG:  disable_query_log=1; break;
      case Q_ENABLE_RESULT_LOG:  disable_result_log=0; break;
      case Q_DISABLE_RESULT_LOG: disable_result_log=1; break;
2718 2719
      case Q_ENABLE_WARNINGS:    disable_warnings=0; break;
      case Q_DISABLE_WARNINGS:   disable_warnings=1; break;
2720 2721
      case Q_ENABLE_INFO:        disable_info=0; break;
      case Q_DISABLE_INFO:       disable_info=1; break;
2722
      case Q_ENABLE_METADATA:    display_metadata=1; break;
2723
      case Q_DISABLE_METADATA:   display_metadata=0; break;
2724
      case Q_SOURCE: do_source(q); break;
2725 2726
      case Q_SLEEP: do_sleep(q, 0); break;
      case Q_REAL_SLEEP: do_sleep(q, 1); break;
2727
      case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(q); break;
2728
      case Q_REQUIRE_MANAGER: do_require_manager(q); break;
2729
#ifndef EMBEDDED_LIBRARY
2730 2731
      case Q_SERVER_START: do_server_start(q); break;
      case Q_SERVER_STOP: do_server_stop(q); break;
2732
#endif
2733 2734 2735 2736
      case Q_INC: do_inc(q); break;
      case Q_DEC: do_dec(q); break;
      case Q_ECHO: do_echo(q); break;
      case Q_SYSTEM: do_system(q); break;
2737 2738 2739 2740
      case Q_DELIMITER:
	strmake(delimiter, q->first_argument, sizeof(delimiter) - 1);
	delimiter_length= strlen(delimiter);
	break;
2741
      case Q_DISPLAY_VERTICAL_RESULTS: display_result_vertically= TRUE; break;
2742
      case Q_DISPLAY_HORIZONTAL_RESULTS: 
2743
	display_result_vertically= FALSE; break;
2744
      case Q_LET: do_let(q); break;
2745
      case Q_EVAL_RESULT: eval_result = 1; break;
2746
      case Q_EVAL:
2747
	if (q->query == q->query_buf)
monty@mysql.com's avatar
monty@mysql.com committed
2748
        {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2749
	  q->query= q->first_argument;
monty@mysql.com's avatar
monty@mysql.com committed
2750 2751
          q->first_word_len= 0;
        }
2752
	/* fall through */
2753
      case Q_QUERY_VERTICAL:
2754
      case Q_QUERY_HORIZONTAL:
2755 2756 2757 2758 2759 2760
      {
	my_bool old_display_result_vertically= display_result_vertically;
	if (!q->query[q->first_word_len])
	{
	  /* This happens when we use 'query_..' on it's own line */
	  q_send_flag=1;
monty@mysql.com's avatar
monty@mysql.com committed
2761 2762 2763
          DBUG_PRINT("info",
                     ("query: '%s' first_word_len: %d  send_flag=1",
                      q->query, q->first_word_len));
2764 2765 2766 2767 2768
	  break;
	}
	/* fix up query pointer if this is * first iteration for this line */
	if (q->query == q->query_buf)
	  q->query += q->first_word_len + 1;
2769
	display_result_vertically= (q->type==Q_QUERY_VERTICAL);
monty@mysql.com's avatar
monty@mysql.com committed
2770
	error|= run_query(&cur_con->mysql, q, QUERY_REAP|QUERY_SEND);
2771 2772 2773
	display_result_vertically= old_display_result_vertically;
	break;
      }
2774
      case Q_QUERY:
2775
      case Q_REAP:
2776
      {
2777 2778 2779 2780 2781
	/*
	  We read the result always regardless of the mode for both full
	  query and read-result only (reap)
	*/
	int flags = QUERY_REAP;
2782
	if (q->type != Q_REAP) /* for a full query, enable the send stage */
2783
	  flags |= QUERY_SEND;
2784 2785 2786 2787 2788
	if (q_send_flag)
	{
	  flags= QUERY_SEND;
	  q_send_flag=0;
	}
2789
	if (save_file[0])
2790
	{
2791 2792 2793
	  strmov(q->record_file,save_file);
	  q->require_file=require_file;
	  save_file[0]=0;
2794
	}
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
2795
	error |= run_query(&cur_con->mysql, q, flags);
2796
	break;
2797
      }
2798
      case Q_SEND:
2799 2800 2801 2802 2803 2804 2805 2806
	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)
2807
	  q->query += q->first_word_len;
2808 2809 2810 2811 2812
	/*
	  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.
2813
	 */
2814
	error |= run_query(&cur_con->mysql, q, QUERY_SEND);
2815
	break;
2816 2817 2818 2819
      case Q_RESULT:
	get_file_name(save_file,q);
	require_file=0;
	break;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2820
      case Q_ERROR:
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2821
	global_expected_errors=get_ints(global_expected_errno,q);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2822
	break;
2823 2824 2825 2826
      case Q_REQUIRE:
	get_file_name(save_file,q);
	require_file=1;
	break;
2827 2828 2829
      case Q_REPLACE:
	get_replace(q);
	break;
2830 2831 2832
      case Q_REPLACE_COLUMN:
	get_replace_column(q);
	break;
2833 2834
      case Q_SAVE_MASTER_POS: do_save_master_pos(); break;
      case Q_SYNC_WITH_MASTER: do_sync_with_master(q); break;
2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847
      case Q_SYNC_SLAVE_WITH_MASTER:
      {
	do_save_master_pos();
	if (*q->first_argument)
	  select_connection(q->first_argument);
	else
	{
	  char buf[] = "slave";
	  select_connection(buf);
	}
	do_sync_with_master2("");
	break;
      }
2848
      case Q_COMMENT:				/* Ignore row */
2849
      case Q_COMMENT_WITH_COMMAND:
2850
	break;
2851 2852 2853
      case Q_PING:
	(void) mysql_ping(&cur_con->mysql);
	break;
2854
      case Q_EXEC: 
kent@mysql.com's avatar
kent@mysql.com committed
2855
	do_exec(q);
2856
	break;
kent@mysql.com's avatar
kent@mysql.com committed
2857 2858 2859 2860 2861 2862 2863 2864 2865
      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;
2866 2867 2868
      default: processed = 0; break;
      }
    }
2869

2870 2871 2872
    if (!processed)
    {
      current_line_inc = 0;
2873
      switch (q->type) {
2874 2875 2876 2877
      case Q_WHILE: do_while(q); break;
      case Q_END_BLOCK: do_done(q); break;
      default: current_line_inc = 1; break;
      }
2878 2879
    }

2880 2881 2882
    parser.current_line += current_line_inc;
  }

2883
  if (result_file && ds_res.length)
2884
  {
2885
    if (!record)
2886 2887
      error |= check_result(&ds_res, result_file, q->require_file);
    else
2888
      str_to_file(result_file, ds_res.str, ds_res.length);
2889
  }
2890
  dynstr_free(&ds_res);
2891

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2892 2893
  if (!silent)
  {
2894
    if (error)
2895 2896 2897 2898
      printf("not ok\n");
    else
      printf("ok\n");
  }
2899

kent@mysql.com's avatar
kent@mysql.com committed
2900 2901
  if (!got_end_timer)
    timer_output();				/* No end_timer cmd, end it */
2902
  free_used_memory();
2903 2904
  exit(error ? 1 : 0);
  return error ? 1 : 0;				/* Keep compiler happy */
2905
  }
2906
}
2907

2908

2909 2910 2911 2912 2913 2914
/*
  Read arguments for embedded server and put them into
  embedded_server_args_count and embedded_server_args[]
*/


monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2915
static int read_server_arguments(const char *name)
2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931
{
  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 */
  }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2932
  if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953
    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;
}
2954

kent@mysql.com's avatar
kent@mysql.com committed
2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993
/****************************************************************************\
 *
 *  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)
  {
    char buf[1024];
    ulonglong timer= timer_now() - timer_start;
    sprintf(buf,"%llu",timer);
    str_to_file(timer_file,buf,strlen(buf));
  }
}

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

2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006
/****************************************************************************
* 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 {
3007
  bool	 found;
3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112
  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 {
3113 3114
  uint	*bits;				/* Pointer to used sets */
  short next[LAST_CHAR_CODE];		/* Pointer to next sets */
3115 3116
  uint	found_len;			/* Best match to date */
  int	found_offset;
3117 3118
  uint	table_offset;
  uint	size_of_bits;			/* For convinience */
3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648
} REP_SET;

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

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

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


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

static uint found_sets=0;


	/* Init a replace structure for further calls */

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

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

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

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


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

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

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

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

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

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

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

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

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


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

	/* Make help sets invisible for nicer codeing */

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

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

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

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

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

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


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

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

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


	/* Get next set bit from set. */

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

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

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

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

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

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

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

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

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

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


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


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

3649
uint replace_strings(REPLACE *rep, my_string *start,uint *max_length,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3650
		     const char *from)
3651 3652 3653
{
  reg1 REPLACE *rep_pos;
  reg2 REPLACE_STRING *rep_str;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3654
  my_string to,end,pos,new_str;
3655 3656 3657

  end=(to= *start) + *max_length-1;
  rep_pos=rep+1;
3658
  for (;;)
3659 3660 3661 3662 3663 3664 3665
  {
    while (!rep_pos->found)
    {
      rep_pos= rep_pos->next[(uchar) *from];
      if (to == end)
      {
	(*max_length)+=8192;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3666
	if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME))))
3667
	  return (uint) -1;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3668 3669
	to=new_str+(to - *start);
	end=(*start=new_str)+ *max_length-1;
3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680
      }
      *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;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3681
	if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME))))
3682
	  return (uint) -1;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3683 3684
	to=new_str+(to - *start);
	end=(*start=new_str)+ *max_length-1;
3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705
      }
      *to++= *pos;
    }
    if (!*(from-=rep_str->from_offset) && rep_pos->found != 2)
      return (uint) (to - *start);
    rep_pos=rep;
  }
}

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

static void free_replace_buffer(void)
{
  my_free(out_buff,MYF(MY_WME));
}
3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762


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

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

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

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

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

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

    to= get_string(&buff, &from, q);
    if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS)
      die("Wrong column number to replace_columns in %s\n", q->query);
    if (!*from)
      die("Wrong number of arguments to replace in %s\n", q->query);
    to= get_string(&buff, &from, q);
    my_free(replace_column[column_number-1], MY_ALLOW_ZERO_PTR);
    replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE));
    set_if_bigger(max_replace_column, column_number);
  }
  my_free(start, MYF(0));
}
monty@mysql.com's avatar
monty@mysql.com committed
3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786

#ifdef __NETWARE__

/*
  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;
3787
  char *pos;
monty@mysql.com's avatar
monty@mysql.com committed
3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806

  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++)
3807
        *env_pos++= *str;
monty@mysql.com's avatar
monty@mysql.com committed
3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849
      *env_pos= 0;

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

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


/*
  popen replacement for Netware

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

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

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

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

#undef popen                                    /* Remove wrapper */

3850
FILE *my_popen(const char *cmd, const char *mode __attribute__((unused)))
monty@mysql.com's avatar
monty@mysql.com committed
3851 3852
{
  char *subst_cmd;
3853 3854
  FILE *res_file;

monty@mysql.com's avatar
monty@mysql.com committed
3855 3856 3857 3858 3859 3860 3861
  subst_cmd= subst_env_var(cmd);
  res_file= popen(subst_cmd, "r0");
  my_free(subst_cmd, MYF(0));
  return res_file;
}

#endif /* __NETWARE__ */