~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/* Copyright (C) 2000 MySQL AB
2
3
   This program is free software; you can redistribute it and/or modify
4
   it under the terms of the GNU General Public License as published by
5
   the Free Software Foundation; version 2 of the License.
6
7
   This program is distributed in the hope that it will be useful,
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
   GNU General Public License for more details.
11
12
   You should have received a copy of the GNU General Public License
13
   along with this program; if not, write to the Free Software
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
16
/*
17
  mysqltest
18
19
  Tool used for executing a .test file
20
21
  See the "MySQL Test framework manual" for more information
22
  http://dev.mysql.com/doc/mysqltest/en/index.html
23
24
  Please keep the test framework tools identical in all versions!
25
26
  Written by:
27
  Sasha Pachev <sasha@mysql.com>
28
  Matt Wagner  <matt@mysql.com>
29
  Monty
30
  Jani
31
  Holyfoot
32
*/
33
34
#define MTEST_VERSION "3.3"
35
36
#include "client_priv.h"
37
#include <mysql_version.h>
38
#include <mysqld_error.h>
39
#include <m_ctype.h>
40
#include <my_dir.h>
41
#include <hash.h>
42
#include <stdarg.h>
43
#include <violite.h>
44
#include "my_regex.h" /* Our own version of regex */
45
#ifdef HAVE_SYS_WAIT_H
46
#include <sys/wait.h>
47
#endif
15 by brian
Fix for stat, NETWARE removal
48
49
1 by brian
clean slate
50
51
#define MAX_VAR_NAME_LENGTH    256
52
#define MAX_COLUMNS            256
53
#define MAX_EMBEDDED_SERVER_ARGS 64
54
#define MAX_DELIMITER_LENGTH 16
55
56
/* Flags controlling send and reap */
57
#define QUERY_SEND_FLAG  1
58
#define QUERY_REAP_FLAG  2
59
60
enum {
61
  OPT_SKIP_SAFEMALLOC=OPT_MAX_CLIENT_OPTION,
62
  OPT_PS_PROTOCOL, OPT_SP_PROTOCOL, OPT_CURSOR_PROTOCOL, OPT_VIEW_PROTOCOL,
63
  OPT_MAX_CONNECT_RETRIES, OPT_MARK_PROGRESS, OPT_LOG_DIR, OPT_TAIL_LINES
64
};
65
66
static int record= 0, opt_sleep= -1;
67
static char *opt_db= 0, *opt_pass= 0;
68
const char *opt_user= 0, *opt_host= 0, *unix_sock= 0, *opt_basedir= "./";
69
const char *opt_logdir= "";
70
const char *opt_include= 0, *opt_charsets_dir;
71
static int opt_port= 0;
72
static int opt_max_connect_retries;
73
static my_bool opt_compress= 0, silent= 0, verbose= 0;
74
static my_bool debug_info_flag= 0, debug_check_flag= 0;
75
static my_bool tty_password= 0;
76
static my_bool opt_mark_progress= 0;
77
static my_bool parsing_disabled= 0;
78
static my_bool display_result_vertically= FALSE,
79
  display_metadata= FALSE, display_result_sorted= FALSE;
80
static my_bool disable_query_log= 0, disable_result_log= 0;
81
static my_bool disable_warnings= 0;
82
static my_bool disable_info= 1;
83
static my_bool abort_on_error= 1;
84
static my_bool server_initialized= 0;
85
static my_bool is_windows= 0;
86
static char **default_argv;
87
static const char *load_default_groups[]= { "mysqltest", "client", 0 };
88
static char line_buffer[MAX_DELIMITER_LENGTH], *line_buffer_pos= line_buffer;
89
90
static uint start_lineno= 0; /* Start line of current command */
91
static uint my_end_arg= 0;
92
93
/* Number of lines of the result to include in failure report */
94
static uint opt_tail_lines= 0;
95
96
static char delimiter[MAX_DELIMITER_LENGTH]= ";";
97
static uint delimiter_length= 1;
98
99
static char TMPDIR[FN_REFLEN];
100
101
/* Block stack */
102
enum block_cmd {
103
  cmd_none,
104
  cmd_if,
105
  cmd_while
106
};
107
108
struct st_block
109
{
110
  int             line; /* Start line of block */
111
  my_bool         ok;   /* Should block be executed */
112
  enum block_cmd  cmd;  /* Command owning the block */
113
};
114
115
static struct st_block block_stack[32];
116
static struct st_block *cur_block, *block_stack_end;
117
118
/* Open file stack */
119
struct st_test_file
120
{
121
  FILE* file;
122
  const char *file_name;
123
  uint lineno; /* Current line in file */
124
};
125
126
static struct st_test_file file_stack[16];
127
static struct st_test_file* cur_file;
128
static struct st_test_file* file_stack_end;
129
130
131
static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */
132
133
static const char *embedded_server_groups[]=
134
{
135
  "server",
136
  "embedded",
137
  "mysqltest_SERVER",
138
  NullS
139
};
140
141
static int embedded_server_arg_count=0;
142
static char *embedded_server_args[MAX_EMBEDDED_SERVER_ARGS];
143
144
/*
145
  Timer related variables
146
  See the timer_output() definition for details
147
*/
148
static char *timer_file = NULL;
149
static ulonglong timer_start;
150
static void timer_output(void);
151
static ulonglong timer_now(void);
152
153
static ulonglong progress_start= 0;
154
155
static void free_re(void);
156
157
DYNAMIC_ARRAY q_lines;
158
159
struct
160
{
161
  int read_lines,current_line;
162
} parser;
163
164
struct
165
{
166
  char file[FN_REFLEN];
167
  ulong pos;
168
} master_pos;
169
170
/* if set, all results are concated and compared against this file */
171
const char *result_file_name= 0;
172
173
typedef struct st_var
174
{
175
  char *name;
176
  int name_len;
177
  char *str_val;
178
  int str_val_len;
179
  int int_val;
180
  int alloced_len;
181
  int int_dirty; /* do not update string if int is updated until first read */
182
  int alloced;
183
  char *env_s;
184
} VAR;
185
186
/*Perl/shell-like variable registers */
187
VAR var_reg[10];
188
189
HASH var_hash;
190
191
struct st_connection
192
{
193
  MYSQL mysql;
194
  /* Used when creating views and sp, to avoid implicit commit */
195
  MYSQL* util_mysql;
196
  char *name;
197
};
198
struct st_connection connections[128];
199
struct st_connection* cur_con= NULL, *next_con, *connections_end;
200
201
/*
202
  List of commands in mysqltest
203
  Must match the "command_names" array
204
  Add new commands before Q_UNKNOWN!
205
*/
206
enum enum_commands {
207
  Q_CONNECTION=1,     Q_QUERY,
208
  Q_CONNECT,	    Q_SLEEP, Q_REAL_SLEEP,
209
  Q_INC,		    Q_DEC,
210
  Q_SOURCE,	    Q_DISCONNECT,
211
  Q_LET,		    Q_ECHO,
212
  Q_WHILE,	    Q_END_BLOCK,
213
  Q_SYSTEM,	    Q_RESULT,
214
  Q_REQUIRE,	    Q_SAVE_MASTER_POS,
215
  Q_SYNC_WITH_MASTER,
216
  Q_SYNC_SLAVE_WITH_MASTER,
217
  Q_ERROR,
218
  Q_SEND,		    Q_REAP,
219
  Q_DIRTY_CLOSE,	    Q_REPLACE, Q_REPLACE_COLUMN,
220
  Q_PING,		    Q_EVAL,
221
  Q_EVAL_RESULT,
222
  Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
223
  Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
224
  Q_WAIT_FOR_SLAVE_TO_STOP,
225
  Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
226
  Q_ENABLE_INFO, Q_DISABLE_INFO,
227
  Q_ENABLE_METADATA, Q_DISABLE_METADATA,
228
  Q_EXEC, Q_DELIMITER,
229
  Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR,
230
  Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
231
  Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, Q_SORTED_RESULT,
232
  Q_START_TIMER, Q_END_TIMER,
233
  Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL,
234
  Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT,
235
  Q_IF,
236
  Q_DISABLE_PARSING, Q_ENABLE_PARSING,
237
  Q_REPLACE_REGEX, Q_REMOVE_FILE, Q_FILE_EXIST,
238
  Q_WRITE_FILE, Q_COPY_FILE, Q_PERL, Q_DIE, Q_EXIT, Q_SKIP,
239
  Q_CHMOD_FILE, Q_APPEND_FILE, Q_CAT_FILE, Q_DIFF_FILES,
240
  Q_SEND_QUIT, Q_CHANGE_USER, Q_MKDIR, Q_RMDIR,
241
242
  Q_UNKNOWN,			       /* Unknown command.   */
243
  Q_COMMENT,			       /* Comments, ignored. */
244
  Q_COMMENT_WITH_COMMAND
245
};
246
247
248
const char *command_names[]=
249
{
250
  "connection",
251
  "query",
252
  "connect",
253
  "sleep",
254
  "real_sleep",
255
  "inc",
256
  "dec",
257
  "source",
258
  "disconnect",
259
  "let",
260
  "echo",
261
  "while",
262
  "end",
263
  "system",
264
  "result",
265
  "require",
266
  "save_master_pos",
267
  "sync_with_master",
268
  "sync_slave_with_master",
269
  "error",
270
  "send",
271
  "reap",
272
  "dirty_close",
273
  "replace_result",
274
  "replace_column",
275
  "ping",
276
  "eval",
277
  "eval_result",
278
  /* Enable/disable that the _query_ is logged to result file */
279
  "enable_query_log",
280
  "disable_query_log",
281
  /* Enable/disable that the _result_ from a query is logged to result file */
282
  "enable_result_log",
283
  "disable_result_log",
284
  "wait_for_slave_to_stop",
285
  "enable_warnings",
286
  "disable_warnings",
287
  "enable_info",
288
  "disable_info",
289
  "enable_metadata",
290
  "disable_metadata",
291
  "exec",
292
  "delimiter",
293
  "disable_abort_on_error",
294
  "enable_abort_on_error",
295
  "vertical_results",
296
  "horizontal_results",
297
  "query_vertical",
298
  "query_horizontal",
299
  "sorted_result",
300
  "start_timer",
301
  "end_timer",
302
  "character_set",
303
  "disable_reconnect",
304
  "enable_reconnect",
305
  "if",
306
  "disable_parsing",
307
  "enable_parsing",
308
  "replace_regex",
309
  "remove_file",
310
  "file_exists",
311
  "write_file",
312
  "copy_file",
313
  "perl",
314
  "die",
315
               
316
  /* Don't execute any more commands, compare result */
317
  "exit",
318
  "skip",
319
  "chmod",
320
  "append_file",
321
  "cat_file",
322
  "diff_files",
323
  "send_quit",
324
  "change_user",
325
  "mkdir",
326
  "rmdir",
327
328
  0
329
};
330
331
332
/*
333
  The list of error codes to --error are stored in an internal array of
334
  structs. This struct can hold numeric SQL error codes, error names or
335
  SQLSTATE codes as strings. The element next to the last active element
336
  in the list is set to type ERR_EMPTY. When an SQL statement returns an
337
  error, we use this list to check if this is an expected error.
338
*/
339
enum match_err_type
340
{
341
  ERR_EMPTY= 0,
342
  ERR_ERRNO,
343
  ERR_SQLSTATE
344
};
345
346
struct st_match_err
347
{
348
  enum match_err_type type;
349
  union
350
  {
351
    uint errnum;
352
    char sqlstate[SQLSTATE_LENGTH+1];  /* \0 terminated string */
353
  } code;
354
};
355
356
struct st_expected_errors
357
{
358
  struct st_match_err err[10];
359
  uint count;
360
};
361
static struct st_expected_errors saved_expected_errors;
362
363
struct st_command
364
{
365
  char *query, *query_buf,*first_argument,*last_argument,*end;
366
  int first_word_len, query_len;
367
  my_bool abort_on_error;
368
  struct st_expected_errors expected_errors;
369
  char require_file[FN_REFLEN];
370
  enum enum_commands type;
371
};
372
373
TYPELIB command_typelib= {array_elements(command_names),"",
374
			  command_names, 0};
375
376
DYNAMIC_STRING ds_res, ds_progress, ds_warning_messages;
377
378
char builtin_echo[FN_REFLEN];
379
380
void die(const char *fmt, ...)
381
  ATTRIBUTE_FORMAT(printf, 1, 2);
382
void abort_not_supported_test(const char *fmt, ...)
383
  ATTRIBUTE_FORMAT(printf, 1, 2);
384
void verbose_msg(const char *fmt, ...)
385
  ATTRIBUTE_FORMAT(printf, 1, 2);
386
void warning_msg(const char *fmt, ...)
387
  ATTRIBUTE_FORMAT(printf, 1, 2);
388
void log_msg(const char *fmt, ...)
389
  ATTRIBUTE_FORMAT(printf, 1, 2);
390
391
VAR* var_from_env(const char *, const char *);
392
VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
393
              int val_len);
394
void var_free(void* v);
395
VAR* var_get(const char *var_name, const char** var_name_end,
396
             my_bool raw, my_bool ignore_not_existing);
397
void eval_expr(VAR* v, const char *p, const char** p_end);
398
my_bool match_delimiter(int c, const char *delim, uint length);
399
void dump_result_to_reject_file(char *buf, int size);
400
void dump_result_to_log_file(char *buf, int size);
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
401
void dump_warning_messages(void);
402
void dump_progress(void);
1 by brian
clean slate
403
404
void do_eval(DYNAMIC_STRING *query_eval, const char *query,
405
             const char *query_end, my_bool pass_through_escape_chars);
406
void str_to_file(const char *fname, char *str, int size);
407
void str_to_file2(const char *fname, char *str, int size, my_bool append);
408
409
/* For replace_column */
410
static char *replace_column[MAX_COLUMNS];
411
static uint max_replace_column= 0;
412
void do_get_replace_column(struct st_command*);
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
413
void free_replace_column(void);
1 by brian
clean slate
414
415
/* For replace */
416
void do_get_replace(struct st_command *command);
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
417
void free_replace(void);
1 by brian
clean slate
418
419
/* For replace_regex */
420
void do_get_replace_regex(struct st_command *command);
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
421
void free_replace_regex(void);
422
423
424
void free_all_replace(void);
425
426
427
void free_all_replace(void){
1 by brian
clean slate
428
  free_replace();
429
  free_replace_regex();
430
  free_replace_column();
431
}
432
433
void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
434
                               int len);
435
void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val);
436
void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val);
437
void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING* ds_input);
438
439
void handle_error(struct st_command*,
440
                  unsigned int err_errno, const char *err_error,
441
                  const char *err_sqlstate, DYNAMIC_STRING *ds);
442
void handle_no_error(struct st_command*);
443
444
#ifdef EMBEDDED_LIBRARY
445
446
/* attributes of the query thread */
447
pthread_attr_t cn_thd_attrib;
448
449
/*
450
  send_one_query executes query in separate thread, which is
451
  necessary in embedded library to run 'send' in proper way.
452
  This implementation doesn't handle errors returned
453
  by mysql_send_query. It's technically possible, though
454
  I don't see where it is needed.
455
*/
456
pthread_handler_t send_one_query(void *arg)
457
{
458
  struct st_connection *cn= (struct st_connection*)arg;
459
460
  mysql_thread_init();
461
  VOID(mysql_send_query(&cn->mysql, cn->cur_query, cn->cur_query_len));
462
463
  mysql_thread_end();
464
  pthread_mutex_lock(&cn->mutex);
465
  cn->query_done= 1;
466
  VOID(pthread_cond_signal(&cn->cond));
467
  pthread_mutex_unlock(&cn->mutex);
468
  pthread_exit(0);
469
  return 0;
470
}
471
472
static int do_send_query(struct st_connection *cn, const char *q, int q_len,
473
                         int flags)
474
{
475
  pthread_t tid;
476
477
  if (flags & QUERY_REAP_FLAG)
478
    return mysql_send_query(&cn->mysql, q, q_len);
479
480
  if (pthread_mutex_init(&cn->mutex, NULL) ||
481
      pthread_cond_init(&cn->cond, NULL))
482
    die("Error in the thread library");
483
484
  cn->cur_query= q;
485
  cn->cur_query_len= q_len;
486
  cn->query_done= 0;
487
  if (pthread_create(&tid, &cn_thd_attrib, send_one_query, (void*)cn))
488
    die("Cannot start new thread for query");
489
490
  return 0;
491
}
492
493
static void wait_query_thread_end(struct st_connection *con)
494
{
495
  if (!con->query_done)
496
  {
497
    pthread_mutex_lock(&con->mutex);
498
    while (!con->query_done)
499
      pthread_cond_wait(&con->cond, &con->mutex);
500
    pthread_mutex_unlock(&con->mutex);
501
  }
502
}
503
504
#else /*EMBEDDED_LIBRARY*/
505
506
#define do_send_query(cn,q,q_len,flags) mysql_send_query(&cn->mysql, q, q_len)
507
508
#endif /*EMBEDDED_LIBRARY*/
509
510
void do_eval(DYNAMIC_STRING *query_eval, const char *query,
511
             const char *query_end, my_bool pass_through_escape_chars)
512
{
513
  const char *p;
514
  register char c, next_c;
515
  register int escaped = 0;
516
  VAR *v;
517
  DBUG_ENTER("do_eval");
518
519
  for (p= query; (c= *p) && p < query_end; ++p)
520
  {
521
    switch(c) {
522
    case '$':
523
      if (escaped)
524
      {
525
	escaped= 0;
526
	dynstr_append_mem(query_eval, p, 1);
527
      }
528
      else
529
      {
530
	if (!(v= var_get(p, &p, 0, 0)))
531
	  die("Bad variable in eval");
532
	dynstr_append_mem(query_eval, v->str_val, v->str_val_len);
533
      }
534
      break;
535
    case '\\':
536
      next_c= *(p+1);
537
      if (escaped)
538
      {
539
	escaped= 0;
540
	dynstr_append_mem(query_eval, p, 1);
541
      }
542
      else if (next_c == '\\' || next_c == '$' || next_c == '"')
543
      {
544
        /* Set escaped only if next char is \, " or $ */
545
	escaped= 1;
546
547
        if (pass_through_escape_chars)
548
        {
549
          /* The escape char should be added to the output string. */
550
          dynstr_append_mem(query_eval, p, 1);
551
        }
552
      }
553
      else
554
	dynstr_append_mem(query_eval, p, 1);
555
      break;
556
    default:
557
      escaped= 0;
558
      dynstr_append_mem(query_eval, p, 1);
559
      break;
560
    }
561
  }
562
  DBUG_VOID_RETURN;
563
}
564
565
566
/*
567
  Run query and dump the result to stdout in vertical format
568
569
  NOTE! This function should be safe to call when an error
570
  has occured and thus any further errors will be ignored(although logged)
571
572
  SYNOPSIS
573
  show_query
574
  mysql - connection to use
575
  query - query to run
576
577
*/
578
579
static void show_query(MYSQL* mysql, const char* query)
580
{
581
  MYSQL_RES* res;
582
  DBUG_ENTER("show_query");
583
584
  if (!mysql)
585
    DBUG_VOID_RETURN;
586
587
  if (mysql_query(mysql, query))
588
  {
589
    log_msg("Error running query '%s': %d %s",
590
            query, mysql_errno(mysql), mysql_error(mysql));
591
    DBUG_VOID_RETURN;
592
  }
593
594
  if ((res= mysql_store_result(mysql)) == NULL)
595
  {
596
    /* No result set returned */
597
    DBUG_VOID_RETURN;
598
  }
599
600
  {
601
    MYSQL_ROW row;
602
    unsigned int i;
603
    unsigned int row_num= 0;
604
    unsigned int num_fields= mysql_num_fields(res);
605
    MYSQL_FIELD *fields= mysql_fetch_fields(res);
606
607
    fprintf(stderr, "=== %s ===\n", query);
608
    while ((row= mysql_fetch_row(res)))
609
    {
610
      unsigned long *lengths= mysql_fetch_lengths(res);
611
      row_num++;
612
613
      fprintf(stderr, "---- %d. ----\n", row_num);
614
      for(i= 0; i < num_fields; i++)
615
      {
616
        fprintf(stderr, "%s\t%.*s\n",
617
                fields[i].name,
618
                (int)lengths[i], row[i] ? row[i] : "NULL");
619
      }
620
    }
621
    for (i= 0; i < strlen(query)+8; i++)
622
      fprintf(stderr, "=");
623
    fprintf(stderr, "\n\n");
624
  }
625
  mysql_free_result(res);
626
627
  DBUG_VOID_RETURN;
628
}
629
630
631
/*
632
  Show any warnings just before the error. Since the last error
633
  is added to the warning stack, only print @@warning_count-1 warnings.
634
635
  NOTE! This function should be safe to call when an error
636
  has occured and this any further errors will be ignored(although logged)
637
638
  SYNOPSIS
639
  show_warnings_before_error
640
  mysql - connection to use
641
642
*/
643
644
static void show_warnings_before_error(MYSQL* mysql)
645
{
646
  MYSQL_RES* res;
647
  const char* query= "SHOW WARNINGS";
648
  DBUG_ENTER("show_warnings_before_error");
649
650
  if (!mysql)
651
    DBUG_VOID_RETURN;
652
653
  if (mysql_query(mysql, query))
654
  {
655
    log_msg("Error running query '%s': %d %s",
656
            query, mysql_errno(mysql), mysql_error(mysql));
657
    DBUG_VOID_RETURN;
658
  }
659
660
  if ((res= mysql_store_result(mysql)) == NULL)
661
  {
662
    /* No result set returned */
663
    DBUG_VOID_RETURN;
664
  }
665
666
  if (mysql_num_rows(res) <= 1)
667
  {
668
    /* Don't display the last row, it's "last error" */
669
  }
670
  else
671
  {
672
    MYSQL_ROW row;
673
    unsigned int row_num= 0;
674
    unsigned int num_fields= mysql_num_fields(res);
675
676
    fprintf(stderr, "\nWarnings from just before the error:\n");
677
    while ((row= mysql_fetch_row(res)))
678
    {
679
      unsigned int i;
680
      unsigned long *lengths= mysql_fetch_lengths(res);
681
682
      if (++row_num >= mysql_num_rows(res))
683
      {
684
        /* Don't display the last row, it's "last error" */
685
        break;
686
      }
687
688
      for(i= 0; i < num_fields; i++)
689
      {
690
        fprintf(stderr, "%.*s ", (int)lengths[i],
691
                row[i] ? row[i] : "NULL");
692
      }
693
      fprintf(stderr, "\n");
694
    }
695
  }
696
  mysql_free_result(res);
697
698
  DBUG_VOID_RETURN;
699
}
700
701
702
enum arg_type
703
{
704
  ARG_STRING,
705
  ARG_REST
706
};
707
708
struct command_arg {
709
  const char *argname;       /* Name of argument   */
710
  enum arg_type type;        /* Type of argument   */
711
  my_bool required;          /* Argument required  */
712
  DYNAMIC_STRING *ds;        /* Storage for argument */
713
  const char *description;   /* Description of the argument */
714
};
715
716
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
717
static void check_command_args(struct st_command *command,
718
                               const char *arguments,
719
                               const struct command_arg *args,
720
                               int num_args, const char delimiter_arg)
1 by brian
clean slate
721
{
722
  int i;
723
  const char *ptr= arguments;
724
  const char *start;
725
  DBUG_ENTER("check_command_args");
726
  DBUG_PRINT("enter", ("num_args: %d", num_args));
727
728
  for (i= 0; i < num_args; i++)
729
  {
730
    const struct command_arg *arg= &args[i];
731
732
    switch (arg->type) {
733
      /* A string */
734
    case ARG_STRING:
735
      /* Skip leading spaces */
736
      while (*ptr && *ptr == ' ')
737
        ptr++;
738
      start= ptr;
739
      /* Find end of arg, terminated by "delimiter_arg" */
740
      while (*ptr && *ptr != delimiter_arg)
741
        ptr++;
742
      if (ptr > start)
743
      {
744
        init_dynamic_string(arg->ds, 0, ptr-start, 32);
745
        do_eval(arg->ds, start, ptr, FALSE);
746
      }
747
      else
748
      {
749
        /* Empty string */
750
        init_dynamic_string(arg->ds, "", 0, 0);
751
      }
752
      command->last_argument= (char*)ptr;
753
754
      /* Step past the delimiter */
755
      if (*ptr && *ptr == delimiter_arg)
756
        ptr++;
757
      DBUG_PRINT("info", ("val: %s", arg->ds->str));
758
      break;
759
760
      /* Rest of line */
761
    case ARG_REST:
762
      start= ptr;
763
      init_dynamic_string(arg->ds, 0, command->query_len, 256);
764
      do_eval(arg->ds, start, command->end, FALSE);
765
      command->last_argument= command->end;
766
      DBUG_PRINT("info", ("val: %s", arg->ds->str));
767
      break;
768
769
    default:
770
      DBUG_ASSERT("Unknown argument type");
771
      break;
772
    }
773
774
    /* Check required arg */
775
    if (arg->ds->length == 0 && arg->required)
776
      die("Missing required argument '%s' to command '%.*s'", arg->argname,
777
          command->first_word_len, command->query);
778
779
  }
780
  /* Check for too many arguments passed */
781
  ptr= command->last_argument;
782
  while(ptr <= command->end)
783
  {
784
    if (*ptr && *ptr != ' ')
785
      die("Extra argument '%s' passed to '%.*s'",
786
          ptr, command->first_word_len, command->query);
787
    ptr++;
788
  }
789
  DBUG_VOID_RETURN;
790
}
791
792
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
793
static void handle_command_error(struct st_command *command, uint error)
1 by brian
clean slate
794
{
795
  DBUG_ENTER("handle_command_error");
796
  DBUG_PRINT("enter", ("error: %d", error));
797
  if (error != 0)
798
  {
799
    uint i;
800
801
    if (command->abort_on_error)
802
      die("command \"%.*s\" failed with error %d",
803
          command->first_word_len, command->query, error);
804
    for (i= 0; i < command->expected_errors.count; i++)
805
    {
806
      DBUG_PRINT("info", ("expected error: %d",
807
                          command->expected_errors.err[i].code.errnum));
808
      if ((command->expected_errors.err[i].type == ERR_ERRNO) &&
809
          (command->expected_errors.err[i].code.errnum == error))
810
      {
811
        DBUG_PRINT("info", ("command \"%.*s\" failed with expected error: %d",
812
                            command->first_word_len, command->query, error));
813
        DBUG_VOID_RETURN;
814
      }
815
    }
816
    die("command \"%.*s\" failed with wrong error: %d",
817
        command->first_word_len, command->query, error);
818
  }
819
  else if (command->expected_errors.err[0].type == ERR_ERRNO &&
820
           command->expected_errors.err[0].code.errnum != 0)
821
  {
822
    /* Error code we wanted was != 0, i.e. not an expected success */
823
    die("command \"%.*s\" succeeded - should have failed with errno %d...",
824
        command->first_word_len, command->query,
825
        command->expected_errors.err[0].code.errnum);
826
  }
827
  DBUG_VOID_RETURN;
828
}
829
830
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
831
static void close_connections(void)
1 by brian
clean slate
832
{
833
  DBUG_ENTER("close_connections");
834
  for (--next_con; next_con >= connections; --next_con)
835
  {
836
    mysql_close(&next_con->mysql);
837
    if (next_con->util_mysql)
838
      mysql_close(next_con->util_mysql);
839
    my_free(next_con->name, MYF(MY_ALLOW_ZERO_PTR));
840
  }
841
  DBUG_VOID_RETURN;
842
}
843
844
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
845
static void close_files(void)
1 by brian
clean slate
846
{
847
  DBUG_ENTER("close_files");
848
  for (; cur_file >= file_stack; cur_file--)
849
  {
850
    if (cur_file->file && cur_file->file != stdin)
851
    {
852
      DBUG_PRINT("info", ("closing file: %s", cur_file->file_name));
853
      my_fclose(cur_file->file, MYF(0));
854
    }
855
    my_free((uchar*) cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR));
856
    cur_file->file_name= 0;
857
  }
858
  DBUG_VOID_RETURN;
859
}
860
861
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
862
static void free_used_memory(void)
1 by brian
clean slate
863
{
864
  uint i;
865
  DBUG_ENTER("free_used_memory");
866
867
  close_connections();
868
  close_files();
869
  hash_free(&var_hash);
870
871
  for (i= 0 ; i < q_lines.elements ; i++)
872
  {
873
    struct st_command **q= dynamic_element(&q_lines, i, struct st_command**);
874
    my_free((*q)->query_buf,MYF(MY_ALLOW_ZERO_PTR));
875
    my_free((*q),MYF(0));
876
  }
877
  for (i= 0; i < 10; i++)
878
  {
879
    if (var_reg[i].alloced_len)
880
      my_free(var_reg[i].str_val, MYF(MY_WME));
881
  }
882
  while (embedded_server_arg_count > 1)
883
    my_free(embedded_server_args[--embedded_server_arg_count],MYF(0));
884
  delete_dynamic(&q_lines);
885
  dynstr_free(&ds_res);
886
  dynstr_free(&ds_progress);
887
  dynstr_free(&ds_warning_messages);
888
  free_all_replace();
889
  my_free(opt_pass,MYF(MY_ALLOW_ZERO_PTR));
890
  free_defaults(default_argv);
891
  free_re();
892
893
  /* Only call mysql_server_end if mysql_server_init has been called */
894
  if (server_initialized)
895
    mysql_server_end();
896
897
  /* Don't use DBUG after mysql_server_end() */
898
  return;
899
}
900
901
902
static void cleanup_and_exit(int exit_code)
903
{
904
  free_used_memory();
905
  my_end(my_end_arg);
906
907
  if (!silent) {
908
    switch (exit_code) {
909
    case 1:
910
      printf("not ok\n");
911
      break;
912
    case 0:
913
      printf("ok\n");
914
      break;
915
    case 62:
916
      printf("skipped\n");
917
    break;
918
    default:
919
      printf("unknown exit code: %d\n", exit_code);
920
      DBUG_ASSERT(0);
921
    }
922
  }
923
924
  exit(exit_code);
925
}
926
927
void die(const char *fmt, ...)
928
{
929
  static int dying= 0;
930
  va_list args;
931
  DBUG_ENTER("die");
932
  DBUG_PRINT("enter", ("start_lineno: %d", start_lineno));
933
934
  /*
935
    Protect against dying twice
936
    first time 'die' is called, try to write log files
937
    second time, just exit
938
  */
939
  if (dying)
940
    cleanup_and_exit(1);
941
  dying= 1;
942
943
  /* Print the error message */
944
  fprintf(stderr, "mysqltest: ");
945
  if (cur_file && cur_file != file_stack)
946
    fprintf(stderr, "In included file \"%s\": ",
947
            cur_file->file_name);
948
  if (start_lineno > 0)
949
    fprintf(stderr, "At line %u: ", start_lineno);
950
  if (fmt)
951
  {
952
    va_start(args, fmt);
953
    vfprintf(stderr, fmt, args);
954
    va_end(args);
955
  }
956
  else
957
    fprintf(stderr, "unknown error");
958
  fprintf(stderr, "\n");
959
  fflush(stderr);
960
961
  /* Show results from queries just before failure */
962
  if (ds_res.length && opt_tail_lines)
963
  {
964
    int tail_lines= opt_tail_lines;
965
    char* show_from= ds_res.str + ds_res.length - 1;
966
    while(show_from > ds_res.str && tail_lines > 0 )
967
    {
968
      show_from--;
969
      if (*show_from == '\n')
970
        tail_lines--;
971
    }
972
    fprintf(stderr, "\nThe result from queries just before the failure was:\n");
973
    if (show_from > ds_res.str)
974
      fprintf(stderr, "< snip >");
975
    fprintf(stderr, "%s", show_from);
976
    fflush(stderr);
977
  }
978
979
  /* Dump the result that has been accumulated so far to .log file */
980
  if (result_file_name && ds_res.length)
981
    dump_result_to_log_file(ds_res.str, ds_res.length);
982
983
  /* Dump warning messages */
984
  if (result_file_name && ds_warning_messages.length)
985
    dump_warning_messages();
986
987
  /*
988
    Help debugging by displaying any warnings that might have
989
    been produced prior to the error
990
  */
991
  if (cur_con)
992
    show_warnings_before_error(&cur_con->mysql);
993
994
  cleanup_and_exit(1);
995
}
996
997
998
void abort_not_supported_test(const char *fmt, ...)
999
{
1000
  va_list args;
1001
  struct st_test_file* err_file= cur_file;
1002
  DBUG_ENTER("abort_not_supported_test");
1003
1004
  /* Print include filestack */
1005
  fprintf(stderr, "The test '%s' is not supported by this installation\n",
1006
          file_stack->file_name);
1007
  fprintf(stderr, "Detected in file %s at line %d\n",
1008
          err_file->file_name, err_file->lineno);
1009
  while (err_file != file_stack)
1010
  {
1011
    err_file--;
1012
    fprintf(stderr, "included from %s at line %d\n",
1013
            err_file->file_name, err_file->lineno);
1014
  }
1015
1016
  /* Print error message */
1017
  va_start(args, fmt);
1018
  if (fmt)
1019
  {
1020
    fprintf(stderr, "reason: ");
1021
    vfprintf(stderr, fmt, args);
1022
    fprintf(stderr, "\n");
1023
    fflush(stderr);
1024
  }
1025
  va_end(args);
1026
1027
  cleanup_and_exit(62);
1028
}
1029
1030
1031
void verbose_msg(const char *fmt, ...)
1032
{
1033
  va_list args;
1034
  DBUG_ENTER("verbose_msg");
1035
  if (!verbose)
1036
    DBUG_VOID_RETURN;
1037
1038
  va_start(args, fmt);
1039
  fprintf(stderr, "mysqltest: ");
1040
  if (cur_file && cur_file != file_stack)
1041
    fprintf(stderr, "In included file \"%s\": ",
1042
            cur_file->file_name);
1043
  if (start_lineno != 0)
1044
    fprintf(stderr, "At line %u: ", start_lineno);
1045
  vfprintf(stderr, fmt, args);
1046
  fprintf(stderr, "\n");
1047
  va_end(args);
1048
1049
  DBUG_VOID_RETURN;
1050
}
1051
1052
1053
void warning_msg(const char *fmt, ...)
1054
{
1055
  va_list args;
1056
  char buff[512];
1057
  size_t len;
1058
  DBUG_ENTER("warning_msg");
1059
1060
  va_start(args, fmt);
1061
  dynstr_append(&ds_warning_messages, "mysqltest: ");
1062
  if (start_lineno != 0)
1063
  {
1064
    dynstr_append(&ds_warning_messages, "Warning detected ");
1065
    if (cur_file && cur_file != file_stack)
1066
    {
77.1.18 by Monty Taylor
Removed my_vsnprintf and my_snprintf.
1067
      len= snprintf(buff, sizeof(buff), "in included file %s ",
1 by brian
clean slate
1068
                       cur_file->file_name);
1069
      dynstr_append_mem(&ds_warning_messages,
1070
                        buff, len);
1071
    }
77.1.18 by Monty Taylor
Removed my_vsnprintf and my_snprintf.
1072
    len= snprintf(buff, sizeof(buff), "at line %d: ",
1 by brian
clean slate
1073
                     start_lineno);
1074
    dynstr_append_mem(&ds_warning_messages,
1075
                      buff, len);
1076
  }
1077
77.1.18 by Monty Taylor
Removed my_vsnprintf and my_snprintf.
1078
  len= vsnprintf(buff, sizeof(buff), fmt, args);
1 by brian
clean slate
1079
  dynstr_append_mem(&ds_warning_messages, buff, len);
1080
1081
  dynstr_append(&ds_warning_messages, "\n");
1082
  va_end(args);
1083
1084
  DBUG_VOID_RETURN;
1085
}
1086
1087
1088
void log_msg(const char *fmt, ...)
1089
{
1090
  va_list args;
1091
  char buff[1024];
1092
  size_t len;
1093
  DBUG_ENTER("log_msg");
1094
1095
  va_start(args, fmt);
77.1.18 by Monty Taylor
Removed my_vsnprintf and my_snprintf.
1096
  len= vsnprintf(buff, sizeof(buff)-1, fmt, args);
1 by brian
clean slate
1097
  va_end(args);
1098
1099
  dynstr_append_mem(&ds_res, buff, len);
1100
  dynstr_append(&ds_res, "\n");
1101
1102
  DBUG_VOID_RETURN;
1103
}
1104
1105
1106
/*
1107
  Read a file and append it to ds
1108
1109
  SYNOPSIS
1110
  cat_file
1111
  ds - pointer to dynamic string where to add the files content
1112
  filename - name of the file to read
1113
1114
*/
1115
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1116
static void cat_file(DYNAMIC_STRING* ds, const char* filename)
1 by brian
clean slate
1117
{
1118
  int fd;
1119
  uint len;
1120
  char buff[512];
1121
1122
  if ((fd= my_open(filename, O_RDONLY, MYF(0))) < 0)
1123
    die("Failed to open file '%s'", filename);
1124
  while((len= my_read(fd, (uchar*)&buff,
1125
                      sizeof(buff), MYF(0))) > 0)
1126
  {
1127
    char *p= buff, *start= buff;
1128
    while (p < buff+len)
1129
    {
1130
      /* Convert cr/lf to lf */
1131
      if (*p == '\r' && *(p+1) && *(p+1)== '\n')
1132
      {
1133
        /* Add fake newline instead of cr and output the line */
1134
        *p= '\n';
1135
        p++; /* Step past the "fake" newline */
1136
        dynstr_append_mem(ds, start, p-start);
1137
        p++; /* Step past the "fake" newline */
1138
        start= p;
1139
      }
1140
      else
1141
        p++;
1142
    }
1143
    /* Output any chars that migh be left */
1144
    dynstr_append_mem(ds, start, p-start);
1145
  }
1146
  my_close(fd, MYF(0));
1147
}
1148
1149
1150
/*
1151
  Run the specified command with popen
1152
1153
  SYNOPSIS
1154
  run_command
1155
  cmd - command to execute(should be properly quoted
1156
  ds_res- pointer to dynamic string where to store the result
1157
1158
*/
1159
1160
static int run_command(char* cmd,
1161
                       DYNAMIC_STRING *ds_res)
1162
{
1163
  char buf[512]= {0};
1164
  FILE *res_file;
1165
  int error;
1166
1167
  if (!(res_file= popen(cmd, "r")))
1168
    die("popen(\"%s\", \"r\") failed", cmd);
1169
1170
  while (fgets(buf, sizeof(buf), res_file))
1171
  {
1172
    DBUG_PRINT("info", ("buf: %s", buf));
1173
    if(ds_res)
1174
    {
1175
      /* Save the output of this command in the supplied string */
1176
      dynstr_append(ds_res, buf);
1177
    }
1178
    else
1179
    {
1180
      /* Print it directly on screen */
1181
      fprintf(stdout, "%s", buf);
1182
    }
1183
  }
1184
1185
  error= pclose(res_file);
1186
  return WEXITSTATUS(error);
1187
}
1188
1189
1190
/*
1191
  Run the specified tool with variable number of arguments
1192
1193
  SYNOPSIS
1194
  run_tool
1195
  tool_path - the name of the tool to run
1196
  ds_res - pointer to dynamic string where to store the result
1197
  ... - variable number of arguments that will be properly
1198
        quoted and appended after the tool's name
1199
1200
*/
1201
1202
static int run_tool(const char *tool_path, DYNAMIC_STRING *ds_res, ...)
1203
{
1204
  int ret;
1205
  const char* arg;
1206
  va_list args;
1207
  DYNAMIC_STRING ds_cmdline;
1208
1209
  DBUG_ENTER("run_tool");
1210
  DBUG_PRINT("enter", ("tool_path: %s", tool_path));
1211
15 by brian
Fix for stat, NETWARE removal
1212
  if (init_dynamic_string(&ds_cmdline, "", FN_REFLEN, FN_REFLEN))
1 by brian
clean slate
1213
    die("Out of memory");
1214
1215
  dynstr_append_os_quoted(&ds_cmdline, tool_path, NullS);
1216
  dynstr_append(&ds_cmdline, " ");
1217
1218
  va_start(args, ds_res);
1219
1220
  while ((arg= va_arg(args, char *)))
1221
  {
1222
    /* Options should be os quoted */
1223
    if (strncmp(arg, "--", 2) == 0)
1224
      dynstr_append_os_quoted(&ds_cmdline, arg, NullS);
1225
    else
1226
      dynstr_append(&ds_cmdline, arg);
1227
    dynstr_append(&ds_cmdline, " ");
1228
  }
1229
1230
  va_end(args);
1231
1232
  DBUG_PRINT("info", ("Running: %s", ds_cmdline.str));
1233
  ret= run_command(ds_cmdline.str, ds_res);
1234
  DBUG_PRINT("exit", ("ret: %d", ret));
1235
  dynstr_free(&ds_cmdline);
1236
  DBUG_RETURN(ret);
1237
}
1238
1239
1240
/*
1241
  Show the diff of two files using the systems builtin diff
1242
  command. If no such diff command exist, just dump the content
1243
  of the two files and inform about how to get "diff"
1244
1245
  SYNOPSIS
1246
  show_diff
1247
  ds - pointer to dynamic string where to add the diff(may be NULL)
1248
  filename1 - name of first file
1249
  filename2 - name of second file
1250
1251
*/
1252
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1253
static void show_diff(DYNAMIC_STRING* ds,
1254
                      const char* filename1, const char* filename2)
1 by brian
clean slate
1255
{
1256
1257
  DYNAMIC_STRING ds_tmp;
1258
1259
  if (init_dynamic_string(&ds_tmp, "", 256, 256))
1260
    die("Out of memory");
1261
1262
  /* First try with unified diff */
1263
  if (run_tool("diff",
1264
               &ds_tmp, /* Get output from diff in ds_tmp */
1265
               "-u",
1266
               filename1,
1267
               filename2,
1268
               "2>&1",
1269
               NULL) > 1) /* Most "diff" tools return >1 if error */
1270
  {
1271
    dynstr_set(&ds_tmp, "");
1272
1273
    /* Fallback to context diff with "diff -c" */
1274
    if (run_tool("diff",
1275
                 &ds_tmp, /* Get output from diff in ds_tmp */
1276
                 "-c",
1277
                 filename1,
1278
                 filename2,
1279
                 "2>&1",
1280
                 NULL) > 1) /* Most "diff" tools return >1 if error */
1281
    {
1282
      /*
1283
        Fallback to dump both files to result file and inform
1284
        about installing "diff"
1285
      */
1286
      dynstr_set(&ds_tmp, "");
1287
1288
      dynstr_append(&ds_tmp,
1289
"\n"
1290
"The two files differ but it was not possible to execute 'diff' in\n"
1291
"order to show only the difference, tried both 'diff -u' or 'diff -c'.\n"
1292
"Instead the whole content of the two files was shown for you to diff manually. ;)\n\n"
1293
"To get a better report you should install 'diff' on your system, which you\n"
1294
"for example can get from http://www.gnu.org/software/diffutils/diffutils.html\n"
1295
"\n");
1296
1297
      dynstr_append(&ds_tmp, " --- ");
1298
      dynstr_append(&ds_tmp, filename1);
1299
      dynstr_append(&ds_tmp, " >>>\n");
1300
      cat_file(&ds_tmp, filename1);
1301
      dynstr_append(&ds_tmp, "<<<\n --- ");
1302
      dynstr_append(&ds_tmp, filename1);
1303
      dynstr_append(&ds_tmp, " >>>\n");
1304
      cat_file(&ds_tmp, filename2);
1305
      dynstr_append(&ds_tmp, "<<<<\n");
1306
    }
1307
  }
1308
1309
  if (ds)
1310
  {
1311
    /* Add the diff to output */
1312
    dynstr_append_mem(ds, ds_tmp.str, ds_tmp.length);
1313
  }
1314
  else
1315
  {
1316
    /* Print diff directly to stdout */
1317
    fprintf(stderr, "%s\n", ds_tmp.str);
1318
  }
1319
 
1320
  dynstr_free(&ds_tmp);
1321
1322
}
1323
1324
1325
enum compare_files_result_enum {
1326
   RESULT_OK= 0,
1327
   RESULT_CONTENT_MISMATCH= 1,
1328
   RESULT_LENGTH_MISMATCH= 2
1329
};
1330
1331
/*
1332
  Compare two files, given a fd to the first file and
1333
  name of the second file
1334
1335
  SYNOPSIS
1336
  compare_files2
1337
  fd - Open file descriptor of the first file
1338
  filename2 - Name of second file
1339
1340
  RETURN VALUES
1341
  According to the values in "compare_files_result_enum"
1342
1343
*/
1344
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1345
static int compare_files2(File fd, const char* filename2)
1 by brian
clean slate
1346
{
1347
  int error= RESULT_OK;
1348
  File fd2;
1349
  uint len, len2;
1350
  char buff[512], buff2[512];
1351
1352
  if ((fd2= my_open(filename2, O_RDONLY, MYF(0))) < 0)
1353
  {
1354
    my_close(fd, MYF(0));
1355
    die("Failed to open second file: '%s'", filename2);
1356
  }
1357
  while((len= my_read(fd, (uchar*)&buff,
1358
                      sizeof(buff), MYF(0))) > 0)
1359
  {
1360
    if ((len2= my_read(fd2, (uchar*)&buff2,
1361
                       sizeof(buff2), MYF(0))) < len)
1362
    {
1363
      /* File 2 was smaller */
1364
      error= RESULT_LENGTH_MISMATCH;
1365
      break;
1366
    }
1367
    if (len2 > len)
1368
    {
1369
      /* File 1 was smaller */
1370
      error= RESULT_LENGTH_MISMATCH;
1371
      break;
1372
    }
1373
    if ((memcmp(buff, buff2, len)))
1374
    {
1375
      /* Content of this part differed */
1376
      error= RESULT_CONTENT_MISMATCH;
1377
      break;
1378
    }
1379
  }
1380
  if (!error && my_read(fd2, (uchar*)&buff2,
1381
                        sizeof(buff2), MYF(0)) > 0)
1382
  {
1383
    /* File 1 was smaller */
1384
    error= RESULT_LENGTH_MISMATCH;
1385
  }
1386
1387
  my_close(fd2, MYF(0));
1388
1389
  return error;
1390
}
1391
1392
1393
/*
1394
  Compare two files, given their filenames
1395
1396
  SYNOPSIS
1397
  compare_files
1398
  filename1 - Name of first file
1399
  filename2 - Name of second file
1400
1401
  RETURN VALUES
1402
  See 'compare_files2'
1403
1404
*/
1405
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1406
static int compare_files(const char* filename1, const char* filename2)
1 by brian
clean slate
1407
{
1408
  File fd;
1409
  int error;
1410
1411
  if ((fd= my_open(filename1, O_RDONLY, MYF(0))) < 0)
1412
    die("Failed to open first file: '%s'", filename1);
1413
1414
  error= compare_files2(fd, filename2);
1415
1416
  my_close(fd, MYF(0));
1417
1418
  return error;
1419
}
1420
1421
1422
/*
1423
  Compare content of the string in ds to content of file fname
1424
1425
  SYNOPSIS
1426
  dyn_string_cmp
1427
  ds - Dynamic string containing the string o be compared
1428
  fname - Name of file to compare with
1429
1430
  RETURN VALUES
1431
  See 'compare_files2'
1432
*/
1433
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1434
static int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname)
1 by brian
clean slate
1435
{
1436
  int error;
1437
  File fd;
1438
  char temp_file_path[FN_REFLEN];
1439
1440
  DBUG_ENTER("dyn_string_cmp");
1441
  DBUG_PRINT("enter", ("fname: %s", fname));
1442
1443
  if ((fd= create_temp_file(temp_file_path, NULL,
1444
                            "tmp", O_CREAT | O_SHARE | O_RDWR,
1445
                            MYF(MY_WME))) < 0)
1446
    die("Failed to create temporary file for ds");
1447
1448
  /* Write ds to temporary file and set file pos to beginning*/
1449
  if (my_write(fd, (uchar *) ds->str, ds->length,
1450
               MYF(MY_FNABP | MY_WME)) ||
1451
      my_seek(fd, 0, SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR)
1452
  {
1453
    my_close(fd, MYF(0));
1454
    /* Remove the temporary file */
1455
    my_delete(temp_file_path, MYF(0));
1456
    die("Failed to write file '%s'", temp_file_path);
1457
  }
1458
1459
  error= compare_files2(fd, fname);
1460
1461
  my_close(fd, MYF(0));
1462
  /* Remove the temporary file */
1463
  my_delete(temp_file_path, MYF(0));
1464
1465
  DBUG_RETURN(error);
1466
}
1467
1468
1469
/*
1470
  Check the content of ds against result file
1471
1472
  SYNOPSIS
1473
  check_result
1474
  ds - content to be checked
1475
1476
  RETURN VALUES
1477
  error - the function will not return
1478
1479
*/
1480
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1481
static void check_result(DYNAMIC_STRING* ds)
1 by brian
clean slate
1482
{
1483
  const char* mess= "Result content mismatch\n";
1484
1485
  DBUG_ENTER("check_result");
1486
  DBUG_ASSERT(result_file_name);
1487
  DBUG_PRINT("enter", ("result_file_name: %s", result_file_name));
1488
1489
  if (access(result_file_name, F_OK) != 0)
1490
    die("The specified result file does not exist: '%s'", result_file_name);
1491
1492
  switch (dyn_string_cmp(ds, result_file_name)) {
1493
  case RESULT_OK:
1494
    break; /* ok */
1495
  case RESULT_LENGTH_MISMATCH:
1496
    mess= "Result length mismatch\n";
1497
    /* Fallthrough */
1498
  case RESULT_CONTENT_MISMATCH:
1499
  {
1500
    /*
1501
      Result mismatched, dump results to .reject file
1502
      and then show the diff
1503
    */
1504
    char reject_file[FN_REFLEN];
1505
    size_t reject_length;
1506
    dirname_part(reject_file, result_file_name, &reject_length);
1507
1508
    if (access(reject_file, W_OK) == 0)
1509
    {
1510
      /* Result file directory is writable, save reject file there */
1511
      fn_format(reject_file, result_file_name, NULL,
1512
                ".reject", MY_REPLACE_EXT);
1513
    }
1514
    else
1515
    {
1516
      /* Put reject file in opt_logdir */
1517
      fn_format(reject_file, result_file_name, opt_logdir,
1518
                ".reject", MY_REPLACE_DIR | MY_REPLACE_EXT);
1519
    }
1520
    str_to_file(reject_file, ds->str, ds->length);
1521
1522
    dynstr_set(ds, NULL); /* Don't create a .log file */
1523
1524
    show_diff(NULL, result_file_name, reject_file);
1525
    die(mess);
1526
    break;
1527
  }
1528
  default: /* impossible */
1529
    die("Unknown error code from dyn_string_cmp()");
1530
  }
1531
1532
  DBUG_VOID_RETURN;
1533
}
1534
1535
1536
/*
1537
  Check the content of ds against a require file
1538
  If match fails, abort the test with special error code
1539
  indicating that test is not supported
1540
1541
  SYNOPSIS
1542
  check_require
1543
  ds - content to be checked
1544
  fname - name of file to check against
1545
1546
  RETURN VALUES
1547
  error - the function will not return
1548
1549
*/
1550
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1551
static void check_require(DYNAMIC_STRING* ds, const char *fname)
1 by brian
clean slate
1552
{
1553
  DBUG_ENTER("check_require");
1554
1555
  if (dyn_string_cmp(ds, fname))
1556
  {
1557
    char reason[FN_REFLEN];
1558
    fn_format(reason, fname, "", "", MY_REPLACE_EXT | MY_REPLACE_DIR);
1559
    abort_not_supported_test("Test requires: '%s'", reason);
1560
  }
1561
  DBUG_VOID_RETURN;
1562
}
1563
1564
1565
/*
1566
   Remove surrounding chars from string
1567
1568
   Return 1 if first character is found but not last
1569
*/
1570
static int strip_surrounding(char* str, char c1, char c2)
1571
{
1572
  char* ptr= str;
1573
1574
  /* Check if the first non space character is c1 */
1575
  while(*ptr && my_isspace(charset_info, *ptr))
1576
    ptr++;
1577
  if (*ptr == c1)
1578
  {
1579
    /* Replace it with a space */
1580
    *ptr= ' ';
1581
1582
    /* Last non space charecter should be c2 */
1583
    ptr= strend(str)-1;
1584
    while(*ptr && my_isspace(charset_info, *ptr))
1585
      ptr--;
1586
    if (*ptr == c2)
1587
    {
1588
      /* Replace it with \0 */
1589
      *ptr= 0;
1590
    }
1591
    else
1592
    {
1593
      /* Mismatch detected */
1594
      return 1;
1595
    }
1596
  }
1597
  return 0;
1598
}
1599
1600
1601
static void strip_parentheses(struct st_command *command)
1602
{
1603
  if (strip_surrounding(command->first_argument, '(', ')'))
1604
      die("%.*s - argument list started with '%c' must be ended with '%c'",
1605
          command->first_word_len, command->query, '(', ')');
1606
}
1607
1608
1609
static uchar *get_var_key(const uchar* var, size_t *len,
1610
                          my_bool __attribute__((unused)) t)
1611
{
1612
  register char* key;
1613
  key = ((VAR*)var)->name;
1614
  *len = ((VAR*)var)->name_len;
1615
  return (uchar*)key;
1616
}
1617
1618
1619
VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
1620
              int val_len)
1621
{
1622
  int val_alloc_len;
1623
  VAR *tmp_var;
1624
  if (!name_len && name)
1625
    name_len = strlen(name);
1626
  if (!val_len && val)
1627
    val_len = strlen(val) ;
1628
  val_alloc_len = val_len + 16; /* room to grow */
1629
  if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var)
1630
                                                  + name_len+1, MYF(MY_WME))))
1631
    die("Out of memory");
1632
1633
  tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0;
1634
  tmp_var->alloced = (v == 0);
1635
1636
  if (!(tmp_var->str_val = my_malloc(val_alloc_len+1, MYF(MY_WME))))
1637
    die("Out of memory");
1638
1639
  memcpy(tmp_var->name, name, name_len);
1640
  if (val)
1641
  {
1642
    memcpy(tmp_var->str_val, val, val_len);
1643
    tmp_var->str_val[val_len]= 0;
1644
  }
1645
  tmp_var->name_len = name_len;
1646
  tmp_var->str_val_len = val_len;
1647
  tmp_var->alloced_len = val_alloc_len;
1648
  tmp_var->int_val = (val) ? atoi(val) : 0;
1649
  tmp_var->int_dirty = 0;
1650
  tmp_var->env_s = 0;
1651
  return tmp_var;
1652
}
1653
1654
1655
void var_free(void *v)
1656
{
1657
  my_free(((VAR*) v)->str_val, MYF(MY_WME));
1658
  my_free(((VAR*) v)->env_s, MYF(MY_WME|MY_ALLOW_ZERO_PTR));
1659
  if (((VAR*)v)->alloced)
1660
    my_free(v, MYF(MY_WME));
1661
}
1662
1663
1664
VAR* var_from_env(const char *name, const char *def_val)
1665
{
1666
  const char *tmp;
1667
  VAR *v;
1668
  if (!(tmp = getenv(name)))
1669
    tmp = def_val;
1670
1671
  v = var_init(0, name, strlen(name), tmp, strlen(tmp));
1672
  my_hash_insert(&var_hash, (uchar*)v);
1673
  return v;
1674
}
1675
1676
1677
VAR* var_get(const char *var_name, const char **var_name_end, my_bool raw,
1678
	     my_bool ignore_not_existing)
1679
{
1680
  int digit;
1681
  VAR *v;
1682
  DBUG_ENTER("var_get");
1683
  DBUG_PRINT("enter", ("var_name: %s",var_name));
1684
1685
  if (*var_name != '$')
1686
    goto err;
1687
  digit = *++var_name - '0';
1688
  if (digit < 0 || digit >= 10)
1689
  {
1690
    const char *save_var_name = var_name, *end;
1691
    uint length;
1692
    end = (var_name_end) ? *var_name_end : 0;
1693
    while (my_isvar(charset_info,*var_name) && var_name != end)
1694
      var_name++;
1695
    if (var_name == save_var_name)
1696
    {
1697
      if (ignore_not_existing)
1698
	DBUG_RETURN(0);
1699
      die("Empty variable");
1700
    }
1701
    length= (uint) (var_name - save_var_name);
1702
    if (length >= MAX_VAR_NAME_LENGTH)
1703
      die("Too long variable name: %s", save_var_name);
1704
1705
    if (!(v = (VAR*) hash_search(&var_hash, (const uchar*) save_var_name,
1706
                                            length)))
1707
    {
1708
      char buff[MAX_VAR_NAME_LENGTH+1];
1709
      strmake(buff, save_var_name, length);
1710
      v= var_from_env(buff, "");
1711
    }
1712
    var_name--;	/* Point at last character */
1713
  }
1714
  else
1715
    v = var_reg + digit;
1716
1717
  if (!raw && v->int_dirty)
1718
  {
1719
    sprintf(v->str_val, "%d", v->int_val);
1720
    v->int_dirty = 0;
1721
    v->str_val_len = strlen(v->str_val);
1722
  }
1723
  if (var_name_end)
1724
    *var_name_end = var_name  ;
1725
  DBUG_RETURN(v);
1726
err:
1727
  if (var_name_end)
1728
    *var_name_end = 0;
1729
  die("Unsupported variable name: %s", var_name);
1730
  DBUG_RETURN(0);
1731
}
1732
1733
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1734
static VAR *var_obtain(const char *name, int len)
1 by brian
clean slate
1735
{
1736
  VAR* v;
1737
  if ((v = (VAR*)hash_search(&var_hash, (const uchar *) name, len)))
1738
    return v;
1739
  v = var_init(0, name, len, "", 0);
1740
  my_hash_insert(&var_hash, (uchar*)v);
1741
  return v;
1742
}
1743
1744
1745
/*
1746
  - if variable starts with a $ it is regarded as a local test varable
1747
  - if not it is treated as a environment variable, and the corresponding
1748
  environment variable will be updated
1749
*/
1750
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1751
static void var_set(const char *var_name, const char *var_name_end,
1752
                    const char *var_val, const char *var_val_end)
1 by brian
clean slate
1753
{
1754
  int digit, env_var= 0;
1755
  VAR *v;
1756
  DBUG_ENTER("var_set");
1757
  DBUG_PRINT("enter", ("var_name: '%.*s' = '%.*s' (length: %d)",
1758
                       (int) (var_name_end - var_name), var_name,
1759
                       (int) (var_val_end - var_val), var_val,
1760
                       (int) (var_val_end - var_val)));
1761
1762
  if (*var_name != '$')
1763
    env_var= 1;
1764
  else
1765
    var_name++;
1766
1767
  digit= *var_name - '0';
1768
  if (!(digit < 10 && digit >= 0))
1769
  {
1770
    v= var_obtain(var_name, (uint) (var_name_end - var_name));
1771
  }
1772
  else
1773
    v= var_reg + digit;
1774
1775
  eval_expr(v, var_val, (const char**) &var_val_end);
1776
1777
  if (env_var)
1778
  {
1779
    char buf[1024], *old_env_s= v->env_s;
1780
    if (v->int_dirty)
1781
    {
1782
      sprintf(v->str_val, "%d", v->int_val);
1783
      v->int_dirty= 0;
1784
      v->str_val_len= strlen(v->str_val);
1785
    }
77.1.18 by Monty Taylor
Removed my_vsnprintf and my_snprintf.
1786
    snprintf(buf, sizeof(buf), "%.*s=%.*s",
1 by brian
clean slate
1787
                v->name_len, v->name,
1788
                v->str_val_len, v->str_val);
1789
    if (!(v->env_s= my_strdup(buf, MYF(MY_WME))))
1790
      die("Out of memory");
1791
    putenv(v->env_s);
1792
    my_free(old_env_s, MYF(MY_ALLOW_ZERO_PTR));
1793
  }
1794
  DBUG_VOID_RETURN;
1795
}
1796
1797
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1798
static void var_set_string(const char* name, const char* value)
1 by brian
clean slate
1799
{
1800
  var_set(name, name + strlen(name), value, value + strlen(value));
1801
}
1802
1803
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1804
static void var_set_int(const char* name, int value)
1 by brian
clean slate
1805
{
1806
  char buf[21];
77.1.18 by Monty Taylor
Removed my_vsnprintf and my_snprintf.
1807
  snprintf(buf, sizeof(buf), "%d", value);
1 by brian
clean slate
1808
  var_set_string(name, buf);
1809
}
1810
1811
1812
/*
1813
  Store an integer (typically the returncode of the last SQL)
1814
  statement in the mysqltest builtin variable $mysql_errno
1815
*/
1816
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1817
static void var_set_errno(int sql_errno)
1 by brian
clean slate
1818
{
1819
  var_set_int("$mysql_errno", sql_errno);
1820
}
1821
1822
1823
/*
1824
  Update $mysql_get_server_version variable with version
1825
  of the currently connected server
1826
*/
1827
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1828
static void var_set_mysql_get_server_version(MYSQL* mysql)
1 by brian
clean slate
1829
{
1830
  var_set_int("$mysql_get_server_version", mysql_get_server_version(mysql));
1831
}
1832
1833
1834
/*
1835
  Set variable from the result of a query
1836
1837
  SYNOPSIS
1838
  var_query_set()
1839
  var	        variable to set from query
1840
  query       start of query string to execute
1841
  query_end   end of the query string to execute
1842
1843
1844
  DESCRIPTION
1845
  let @<var_name> = `<query>`
1846
1847
  Execute the query and assign the first row of result to var as
1848
  a tab separated strings
1849
1850
  Also assign each column of the result set to
1851
  variable "$<var_name>_<column_name>"
1852
  Thus the tab separated output can be read from $<var_name> and
1853
  and each individual column can be read as $<var_name>_<col_name>
1854
1855
*/
1856
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1857
static void var_query_set(VAR *var, const char *query, const char** query_end)
1 by brian
clean slate
1858
{
1859
  char *end = (char*)((query_end && *query_end) ?
1860
		      *query_end : query + strlen(query));
1861
  MYSQL_RES *res;
1862
  MYSQL_ROW row;
1863
  MYSQL* mysql = &cur_con->mysql;
1864
  DYNAMIC_STRING ds_query;
1865
  DBUG_ENTER("var_query_set");
1866
1867
  while (end > query && *end != '`')
1868
    --end;
1869
  if (query == end)
1870
    die("Syntax error in query, missing '`'");
1871
  ++query;
1872
1873
  /* Eval the query, thus replacing all environment variables */
1874
  init_dynamic_string(&ds_query, 0, (end - query) + 32, 256);
1875
  do_eval(&ds_query, query, end, FALSE);
1876
1877
  if (mysql_real_query(mysql, ds_query.str, ds_query.length))
1878
    die("Error running query '%s': %d %s", ds_query.str,
1879
	mysql_errno(mysql), mysql_error(mysql));
1880
  if (!(res= mysql_store_result(mysql)))
1881
    die("Query '%s' didn't return a result set", ds_query.str);
1882
  dynstr_free(&ds_query);
1883
1884
  if ((row= mysql_fetch_row(res)) && row[0])
1885
  {
1886
    /*
1887
      Concatenate all fields in the first row with tab in between
1888
      and assign that string to the $variable
1889
    */
1890
    DYNAMIC_STRING result;
1891
    uint i;
1892
    ulong *lengths;
1893
1894
    init_dynamic_string(&result, "", 512, 512);
1895
    lengths= mysql_fetch_lengths(res);
1896
    for (i= 0; i < mysql_num_fields(res); i++)
1897
    {
1898
      if (row[i])
1899
      {
1900
        /* Add column to tab separated string */
1901
	dynstr_append_mem(&result, row[i], lengths[i]);
1902
      }
1903
      dynstr_append_mem(&result, "\t", 1);
1904
    }
1905
    end= result.str + result.length-1;
1906
    eval_expr(var, result.str, (const char**) &end);
1907
    dynstr_free(&result);
1908
  }
1909
  else
1910
    eval_expr(var, "", 0);
1911
1912
  mysql_free_result(res);
1913
  DBUG_VOID_RETURN;
1914
}
1915
1916
1917
/*
1918
  Set variable from the result of a field in a query
1919
1920
  This function is useful when checking for a certain value
1921
  in the output from a query that can't be restricted to only
1922
  return some values. A very good example of that is most SHOW
1923
  commands.
1924
1925
  SYNOPSIS
1926
  var_set_query_get_value()
1927
1928
  DESCRIPTION
1929
  let $variable= query_get_value(<query to run>,<column name>,<row no>);
1930
1931
  <query to run> -    The query that should be sent to the server
1932
  <column name> -     Name of the column that holds the field be compared
1933
                      against the expected value
1934
  <row no> -          Number of the row that holds the field to be
1935
                      compared against the expected value
1936
1937
*/
1938
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1939
static void var_set_query_get_value(struct st_command *command, VAR *var)
1 by brian
clean slate
1940
{
1941
  long row_no;
1942
  int col_no= -1;
1943
  MYSQL_RES* res;
1944
  MYSQL* mysql= &cur_con->mysql;
1945
1946
  static DYNAMIC_STRING ds_query;
1947
  static DYNAMIC_STRING ds_col;
1948
  static DYNAMIC_STRING ds_row;
1949
  const struct command_arg query_get_value_args[] = {
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
1950
    {"query", ARG_STRING, TRUE, &ds_query, "Query to run"},
1951
    {"column name", ARG_STRING, TRUE, &ds_col, "Name of column"},
1952
    {"row number", ARG_STRING, TRUE, &ds_row, "Number for row"}
1 by brian
clean slate
1953
  };
1954
1955
  DBUG_ENTER("var_set_query_get_value");
1956
1957
  strip_parentheses(command);
1958
  DBUG_PRINT("info", ("query: %s", command->query));
1959
  check_command_args(command, command->first_argument, query_get_value_args,
1960
                     sizeof(query_get_value_args)/sizeof(struct command_arg),
1961
                     ',');
1962
1963
  DBUG_PRINT("info", ("query: %s", ds_query.str));
1964
  DBUG_PRINT("info", ("col: %s", ds_col.str));
1965
1966
  /* Convert row number to int */
1967
  if (!str2int(ds_row.str, 10, (long) 0, (long) INT_MAX, &row_no))
1968
    die("Invalid row number: '%s'", ds_row.str);
1969
  DBUG_PRINT("info", ("row: %s, row_no: %ld", ds_row.str, row_no));
1970
  dynstr_free(&ds_row);
1971
1972
  /* Remove any surrounding "'s from the query - if there is any */
1973
  if (strip_surrounding(ds_query.str, '"', '"'))
1974
    die("Mismatched \"'s around query '%s'", ds_query.str);
1975
1976
  /* Run the query */
1977
  if (mysql_real_query(mysql, ds_query.str, ds_query.length))
1978
    die("Error running query '%s': %d %s", ds_query.str,
1979
	mysql_errno(mysql), mysql_error(mysql));
1980
  if (!(res= mysql_store_result(mysql)))
1981
    die("Query '%s' didn't return a result set", ds_query.str);
1982
1983
  {
1984
    /* Find column number from the given column name */
1985
    uint i;
1986
    uint num_fields= mysql_num_fields(res);
1987
    MYSQL_FIELD *fields= mysql_fetch_fields(res);
1988
1989
    for (i= 0; i < num_fields; i++)
1990
    {
1991
      if (strcmp(fields[i].name, ds_col.str) == 0 &&
1992
          strlen(fields[i].name) == ds_col.length)
1993
      {
1994
        col_no= i;
1995
        break;
1996
      }
1997
    }
1998
    if (col_no == -1)
1999
    {
2000
      mysql_free_result(res);
2001
      die("Could not find column '%s' in the result of '%s'",
2002
          ds_col.str, ds_query.str);
2003
    }
2004
    DBUG_PRINT("info", ("Found column %d with name '%s'",
2005
                        i, fields[i].name));
2006
  }
2007
  dynstr_free(&ds_col);
2008
2009
  {
2010
    /* Get the value */
2011
    MYSQL_ROW row;
2012
    long rows= 0;
2013
    const char* value= "No such row";
2014
2015
    while ((row= mysql_fetch_row(res)))
2016
    {
2017
      if (++rows == row_no)
2018
      {
2019
2020
        DBUG_PRINT("info", ("At row %ld, column %d is '%s'",
2021
                            row_no, col_no, row[col_no]));
2022
        /* Found the row to get */
2023
        if (row[col_no])
2024
          value= row[col_no];
2025
        else
2026
          value= "NULL";
2027
2028
        break;
2029
      }
2030
    }
2031
    eval_expr(var, value, 0);
2032
  }
2033
  dynstr_free(&ds_query);
2034
  mysql_free_result(res);
2035
2036
  DBUG_VOID_RETURN;
2037
}
2038
2039
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2040
static void var_copy(VAR *dest, VAR *src)
1 by brian
clean slate
2041
{
2042
  dest->int_val= src->int_val;
2043
  dest->int_dirty= src->int_dirty;
2044
2045
  /* Alloc/realloc data for str_val in dest */
2046
  if (dest->alloced_len < src->alloced_len &&
2047
      !(dest->str_val= dest->str_val
2048
        ? my_realloc(dest->str_val, src->alloced_len, MYF(MY_WME))
2049
        : my_malloc(src->alloced_len, MYF(MY_WME))))
2050
    die("Out of memory");
2051
  else
2052
    dest->alloced_len= src->alloced_len;
2053
2054
  /* Copy str_val data to dest */
2055
  dest->str_val_len= src->str_val_len;
2056
  if (src->str_val_len)
2057
    memcpy(dest->str_val, src->str_val, src->str_val_len);
2058
}
2059
2060
2061
void eval_expr(VAR *v, const char *p, const char **p_end)
2062
{
2063
2064
  DBUG_ENTER("eval_expr");
2065
  DBUG_PRINT("enter", ("p: '%s'", p));
2066
2067
  if (*p == '$')
2068
  {
2069
    VAR *vp;
2070
    if ((vp= var_get(p, p_end, 0, 0)))
2071
      var_copy(v, vp);
2072
    DBUG_VOID_RETURN;
2073
  }
2074
2075
  if (*p == '`')
2076
  {
2077
    var_query_set(v, p, p_end);
2078
    DBUG_VOID_RETURN;
2079
  }
2080
2081
  {
2082
    /* Check if this is a "let $var= query_get_value()" */
2083
    const char* get_value_str= "query_get_value";
2084
    const size_t len= strlen(get_value_str);
2085
    if (strncmp(p, get_value_str, len)==0)
2086
    {
2087
      struct st_command command;
2088
      memset(&command, 0, sizeof(command));
2089
      command.query= (char*)p;
2090
      command.first_word_len= len;
2091
      command.first_argument= command.query + len;
2092
      command.end= (char*)*p_end;
2093
      var_set_query_get_value(&command, v);
2094
      DBUG_VOID_RETURN;
2095
    }
2096
  }
2097
2098
  {
2099
    int new_val_len = (p_end && *p_end) ?
2100
      (int) (*p_end - p) : (int) strlen(p);
2101
    if (new_val_len + 1 >= v->alloced_len)
2102
    {
2103
      static int MIN_VAR_ALLOC= 32;
2104
      v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
2105
        MIN_VAR_ALLOC : new_val_len + 1;
2106
      if (!(v->str_val =
2107
            v->str_val ? my_realloc(v->str_val, v->alloced_len+1,
2108
                                    MYF(MY_WME)) :
2109
            my_malloc(v->alloced_len+1, MYF(MY_WME))))
2110
        die("Out of memory");
2111
    }
2112
    v->str_val_len = new_val_len;
2113
    memcpy(v->str_val, p, new_val_len);
2114
    v->str_val[new_val_len] = 0;
2115
    v->int_val=atoi(p);
2116
    DBUG_PRINT("info", ("atoi on '%s', returns: %d", p, v->int_val));
2117
    v->int_dirty=0;
2118
  }
2119
  DBUG_VOID_RETURN;
2120
}
2121
2122
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2123
static int open_file(const char *name)
1 by brian
clean slate
2124
{
2125
  char buff[FN_REFLEN];
2126
  DBUG_ENTER("open_file");
2127
  DBUG_PRINT("enter", ("name: %s", name));
2128
  if (!test_if_hard_path(name))
2129
  {
2130
    strxmov(buff, opt_basedir, name, NullS);
2131
    name=buff;
2132
  }
2133
  fn_format(buff, name, "", "", MY_UNPACK_FILENAME);
2134
2135
  if (cur_file == file_stack_end)
2136
    die("Source directives are nesting too deep");
2137
  cur_file++;
2138
  if (!(cur_file->file = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0))))
2139
  {
2140
    cur_file--;
2141
    die("Could not open '%s' for reading", buff);
2142
  }
2143
  cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
2144
  cur_file->lineno=1;
2145
  DBUG_RETURN(0);
2146
}
2147
2148
2149
/*
2150
  Source and execute the given file
2151
2152
  SYNOPSIS
2153
  do_source()
2154
  query	called command
2155
2156
  DESCRIPTION
2157
  source <file_name>
2158
2159
  Open the file <file_name> and execute it
2160
2161
*/
2162
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2163
static void do_source(struct st_command *command)
1 by brian
clean slate
2164
{
2165
  static DYNAMIC_STRING ds_filename;
2166
  const struct command_arg source_args[] = {
2167
    { "filename", ARG_STRING, TRUE, &ds_filename, "File to source" }
2168
  };
2169
  DBUG_ENTER("do_source");
2170
2171
  check_command_args(command, command->first_argument, source_args,
2172
                     sizeof(source_args)/sizeof(struct command_arg),
2173
                     ' ');
2174
2175
  /*
2176
    If this file has already been sourced, don't source it again.
2177
    It's already available in the q_lines cache.
2178
  */
2179
  if (parser.current_line < (parser.read_lines - 1))
2180
    ; /* Do nothing */
2181
  else
2182
  {
2183
    DBUG_PRINT("info", ("sourcing file: %s", ds_filename.str));
2184
    open_file(ds_filename.str);
2185
  }
2186
2187
  dynstr_free(&ds_filename);
2188
  return;
2189
}
2190
2191
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2192
static FILE* my_popen(DYNAMIC_STRING *ds_cmd, const char *mode)
1 by brian
clean slate
2193
{
2194
  return popen(ds_cmd->str, mode);
2195
}
2196
2197
2198
static void init_builtin_echo(void)
2199
{
2200
  builtin_echo[0]= 0;
2201
  return;
2202
}
2203
2204
2205
/*
2206
  Replace a substring
2207
2208
  SYNOPSIS
2209
    replace
2210
    ds_str      The string to search and perform the replace in
2211
    search_str  The string to search for
2212
    search_len  Length of the string to search for
2213
    replace_str The string to replace with
2214
    replace_len Length of the string to replace with
2215
2216
  RETURN
2217
    0 String replaced
2218
    1 Could not find search_str in str
2219
*/
2220
2221
static int replace(DYNAMIC_STRING *ds_str,
2222
                   const char *search_str, ulong search_len,
2223
                   const char *replace_str, ulong replace_len)
2224
{
2225
  DYNAMIC_STRING ds_tmp;
2226
  const char *start= strstr(ds_str->str, search_str);
2227
  if (!start)
2228
    return 1;
2229
  init_dynamic_string(&ds_tmp, "",
2230
                      ds_str->length + replace_len, 256);
2231
  dynstr_append_mem(&ds_tmp, ds_str->str, start - ds_str->str);
2232
  dynstr_append_mem(&ds_tmp, replace_str, replace_len);
2233
  dynstr_append(&ds_tmp, start + search_len);
2234
  dynstr_set(ds_str, ds_tmp.str);
2235
  dynstr_free(&ds_tmp);
2236
  return 0;
2237
}
2238
2239
2240
/*
2241
  Execute given command.
2242
2243
  SYNOPSIS
2244
  do_exec()
2245
  query	called command
2246
2247
  DESCRIPTION
2248
  exec <command>
2249
2250
  Execute the text between exec and end of line in a subprocess.
2251
  The error code returned from the subprocess is checked against the
2252
  expected error array, previously set with the --error command.
2253
  It can thus be used to execute a command that shall fail.
2254
2255
  NOTE
2256
  Although mysqltest is executed from cygwin shell, the command will be
2257
  executed in "cmd.exe". Thus commands like "rm" etc can NOT be used, use
2258
  mysqltest commmand(s) like "remove_file" for that
2259
*/
2260
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2261
static void do_exec(struct st_command *command)
1 by brian
clean slate
2262
{
2263
  int error;
2264
  char buf[512];
2265
  FILE *res_file;
2266
  char *cmd= command->first_argument;
2267
  DYNAMIC_STRING ds_cmd;
2268
  DBUG_ENTER("do_exec");
2269
  DBUG_PRINT("enter", ("cmd: '%s'", cmd));
2270
2271
  /* Skip leading space */
2272
  while (*cmd && my_isspace(charset_info, *cmd))
2273
    cmd++;
2274
  if (!*cmd)
2275
    die("Missing argument in exec");
2276
  command->last_argument= command->end;
2277
2278
  init_dynamic_string(&ds_cmd, 0, command->query_len+256, 256);
2279
  /* Eval the command, thus replacing all environment variables */
2280
  do_eval(&ds_cmd, cmd, command->end, !is_windows);
2281
2282
  /* Check if echo should be replaced with "builtin" echo */
2283
  if (builtin_echo[0] && strncmp(cmd, "echo", 4) == 0)
2284
  {
2285
    /* Replace echo with our "builtin" echo */
2286
    replace(&ds_cmd, "echo", 4, builtin_echo, strlen(builtin_echo));
2287
  }
2288
2289
  DBUG_PRINT("info", ("Executing '%s' as '%s'",
2290
                      command->first_argument, ds_cmd.str));
2291
2292
  if (!(res_file= my_popen(&ds_cmd, "r")) && command->abort_on_error)
2293
  {
2294
    dynstr_free(&ds_cmd);
2295
    die("popen(\"%s\", \"r\") failed", command->first_argument);
2296
  }
2297
2298
  while (fgets(buf, sizeof(buf), res_file))
2299
  {
2300
    if (disable_result_log)
2301
    {
2302
      buf[strlen(buf)-1]=0;
2303
      DBUG_PRINT("exec_result",("%s", buf));
2304
    }
2305
    else
2306
    {
2307
      replace_dynstr_append(&ds_res, buf);
2308
    }
2309
  }
2310
  error= pclose(res_file);
2311
  if (error > 0)
2312
  {
2313
    uint status= WEXITSTATUS(error), i;
2314
    my_bool ok= 0;
2315
2316
    if (command->abort_on_error)
2317
    {
2318
      log_msg("exec of '%s' failed, error: %d, status: %d, errno: %d",
2319
              ds_cmd.str, error, status, errno);
2320
      dynstr_free(&ds_cmd);
2321
      die("command \"%s\" failed", command->first_argument);
2322
    }
2323
2324
    DBUG_PRINT("info",
2325
               ("error: %d, status: %d", error, status));
2326
    for (i= 0; i < command->expected_errors.count; i++)
2327
    {
2328
      DBUG_PRINT("info", ("expected error: %d",
2329
                          command->expected_errors.err[i].code.errnum));
2330
      if ((command->expected_errors.err[i].type == ERR_ERRNO) &&
2331
          (command->expected_errors.err[i].code.errnum == status))
2332
      {
2333
        ok= 1;
2334
        DBUG_PRINT("info", ("command \"%s\" failed with expected error: %d",
2335
                            command->first_argument, status));
2336
      }
2337
    }
2338
    if (!ok)
2339
    {
2340
      dynstr_free(&ds_cmd);
2341
      die("command \"%s\" failed with wrong error: %d",
2342
          command->first_argument, status);
2343
    }
2344
  }
2345
  else if (command->expected_errors.err[0].type == ERR_ERRNO &&
2346
           command->expected_errors.err[0].code.errnum != 0)
2347
  {
2348
    /* Error code we wanted was != 0, i.e. not an expected success */
2349
    log_msg("exec of '%s failed, error: %d, errno: %d",
2350
            ds_cmd.str, error, errno);
2351
    dynstr_free(&ds_cmd);
2352
    die("command \"%s\" succeeded - should have failed with errno %d...",
2353
        command->first_argument, command->expected_errors.err[0].code.errnum);
2354
  }
2355
2356
  dynstr_free(&ds_cmd);
2357
  DBUG_VOID_RETURN;
2358
}
2359
2360
enum enum_operator
2361
{
2362
  DO_DEC,
2363
  DO_INC
2364
};
2365
2366
2367
/*
2368
  Decrease or increase the value of a variable
2369
2370
  SYNOPSIS
2371
  do_modify_var()
2372
  query	called command
2373
  operator    operation to perform on the var
2374
2375
  DESCRIPTION
2376
  dec $var_name
2377
  inc $var_name
2378
2379
*/
2380
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2381
static int do_modify_var(struct st_command *command,
2382
                         enum enum_operator operator)
1 by brian
clean slate
2383
{
2384
  const char *p= command->first_argument;
2385
  VAR* v;
2386
  if (!*p)
2387
    die("Missing argument to %.*s", command->first_word_len, command->query);
2388
  if (*p != '$')
2389
    die("The argument to %.*s must be a variable (start with $)",
2390
        command->first_word_len, command->query);
2391
  v= var_get(p, &p, 1, 0);
2392
  switch (operator) {
2393
  case DO_DEC:
2394
    v->int_val--;
2395
    break;
2396
  case DO_INC:
2397
    v->int_val++;
2398
    break;
2399
  default:
2400
    die("Invalid operator to do_modify_var");
2401
    break;
2402
  }
2403
  v->int_dirty= 1;
2404
  command->last_argument= (char*)++p;
2405
  return 0;
2406
}
2407
2408
2409
/*
2410
  Wrapper for 'system' function
2411
2412
  NOTE
2413
  If mysqltest is executed from cygwin shell, the command will be
2414
  executed in the "windows command interpreter" cmd.exe and we prepend "sh"
2415
  to make it be executed by cygwins "bash". Thus commands like "rm",
2416
  "mkdir" as well as shellscripts can executed by "system" in Windows.
2417
2418
*/
2419
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2420
static int my_system(DYNAMIC_STRING* ds_cmd)
1 by brian
clean slate
2421
{
2422
  return system(ds_cmd->str);
2423
}
2424
2425
2426
/*
2427
  SYNOPSIS
2428
  do_system
2429
  command	called command
2430
2431
  DESCRIPTION
2432
  system <command>
2433
2434
  Eval the query to expand any $variables in the command.
2435
  Execute the command with the "system" command.
2436
2437
*/
2438
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2439
static void do_system(struct st_command *command)
1 by brian
clean slate
2440
{
2441
  DYNAMIC_STRING ds_cmd;
2442
  DBUG_ENTER("do_system");
2443
2444
  if (strlen(command->first_argument) == 0)
2445
    die("Missing arguments to system, nothing to do!");
2446
2447
  init_dynamic_string(&ds_cmd, 0, command->query_len + 64, 256);
2448
2449
  /* Eval the system command, thus replacing all environment variables */
2450
  do_eval(&ds_cmd, command->first_argument, command->end, !is_windows);
2451
2452
  DBUG_PRINT("info", ("running system command '%s' as '%s'",
2453
                      command->first_argument, ds_cmd.str));
2454
  if (my_system(&ds_cmd))
2455
  {
2456
    if (command->abort_on_error)
2457
      die("system command '%s' failed", command->first_argument);
2458
2459
    /* If ! abort_on_error, log message and continue */
2460
    dynstr_append(&ds_res, "system command '");
2461
    replace_dynstr_append(&ds_res, command->first_argument);
2462
    dynstr_append(&ds_res, "' failed\n");
2463
  }
2464
2465
  command->last_argument= command->end;
2466
  dynstr_free(&ds_cmd);
2467
  DBUG_VOID_RETURN;
2468
}
2469
2470
2471
/*
2472
  SYNOPSIS
2473
  do_remove_file
2474
  command	called command
2475
2476
  DESCRIPTION
2477
  remove_file <file_name>
2478
  Remove the file <file_name>
2479
*/
2480
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2481
static void do_remove_file(struct st_command *command)
1 by brian
clean slate
2482
{
2483
  int error;
2484
  static DYNAMIC_STRING ds_filename;
2485
  const struct command_arg rm_args[] = {
2486
    { "filename", ARG_STRING, TRUE, &ds_filename, "File to delete" }
2487
  };
2488
  DBUG_ENTER("do_remove_file");
2489
2490
  check_command_args(command, command->first_argument,
2491
                     rm_args, sizeof(rm_args)/sizeof(struct command_arg),
2492
                     ' ');
2493
2494
  DBUG_PRINT("info", ("removing file: %s", ds_filename.str));
2495
  error= my_delete(ds_filename.str, MYF(0)) != 0;
2496
  handle_command_error(command, error);
2497
  dynstr_free(&ds_filename);
2498
  DBUG_VOID_RETURN;
2499
}
2500
2501
2502
/*
2503
  SYNOPSIS
2504
  do_copy_file
2505
  command	command handle
2506
2507
  DESCRIPTION
2508
  copy_file <from_file> <to_file>
2509
  Copy <from_file> to <to_file>
2510
2511
  NOTE! Will fail if <to_file> exists
2512
*/
2513
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2514
static void do_copy_file(struct st_command *command)
1 by brian
clean slate
2515
{
2516
  int error;
2517
  static DYNAMIC_STRING ds_from_file;
2518
  static DYNAMIC_STRING ds_to_file;
2519
  const struct command_arg copy_file_args[] = {
2520
    { "from_file", ARG_STRING, TRUE, &ds_from_file, "Filename to copy from" },
2521
    { "to_file", ARG_STRING, TRUE, &ds_to_file, "Filename to copy to" }
2522
  };
2523
  DBUG_ENTER("do_copy_file");
2524
2525
  check_command_args(command, command->first_argument,
2526
                     copy_file_args,
2527
                     sizeof(copy_file_args)/sizeof(struct command_arg),
2528
                     ' ');
2529
2530
  DBUG_PRINT("info", ("Copy %s to %s", ds_from_file.str, ds_to_file.str));
2531
  error= (my_copy(ds_from_file.str, ds_to_file.str,
2532
                  MYF(MY_DONT_OVERWRITE_FILE)) != 0);
2533
  handle_command_error(command, error);
2534
  dynstr_free(&ds_from_file);
2535
  dynstr_free(&ds_to_file);
2536
  DBUG_VOID_RETURN;
2537
}
2538
2539
2540
/*
2541
  SYNOPSIS
2542
  do_chmod_file
2543
  command	command handle
2544
2545
  DESCRIPTION
2546
  chmod <octal> <file_name>
2547
  Change file permission of <file_name>
2548
2549
*/
2550
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2551
static void do_chmod_file(struct st_command *command)
1 by brian
clean slate
2552
{
2553
  long mode= 0;
2554
  static DYNAMIC_STRING ds_mode;
2555
  static DYNAMIC_STRING ds_file;
2556
  const struct command_arg chmod_file_args[] = {
2557
    { "mode", ARG_STRING, TRUE, &ds_mode, "Mode of file(octal) ex. 0660"}, 
2558
    { "filename", ARG_STRING, TRUE, &ds_file, "Filename of file to modify" }
2559
  };
2560
  DBUG_ENTER("do_chmod_file");
2561
2562
  check_command_args(command, command->first_argument,
2563
                     chmod_file_args,
2564
                     sizeof(chmod_file_args)/sizeof(struct command_arg),
2565
                     ' ');
2566
2567
  /* Parse what mode to set */
2568
  if (ds_mode.length != 4 ||
2569
      str2int(ds_mode.str, 8, 0, INT_MAX, &mode) == NullS)
2570
    die("You must write a 4 digit octal number for mode");
2571
2572
  DBUG_PRINT("info", ("chmod %o %s", (uint)mode, ds_file.str));
2573
  handle_command_error(command, chmod(ds_file.str, mode));
2574
  dynstr_free(&ds_mode);
2575
  dynstr_free(&ds_file);
2576
  DBUG_VOID_RETURN;
2577
}
2578
2579
2580
/*
2581
  SYNOPSIS
2582
  do_file_exists
2583
  command	called command
2584
2585
  DESCRIPTION
2586
  fiile_exist <file_name>
2587
  Check if file <file_name> exists
2588
*/
2589
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2590
static void do_file_exist(struct st_command *command)
1 by brian
clean slate
2591
{
2592
  int error;
2593
  static DYNAMIC_STRING ds_filename;
2594
  const struct command_arg file_exist_args[] = {
2595
    { "filename", ARG_STRING, TRUE, &ds_filename, "File to check if it exist" }
2596
  };
2597
  DBUG_ENTER("do_file_exist");
2598
2599
  check_command_args(command, command->first_argument,
2600
                     file_exist_args,
2601
                     sizeof(file_exist_args)/sizeof(struct command_arg),
2602
                     ' ');
2603
2604
  DBUG_PRINT("info", ("Checking for existence of file: %s", ds_filename.str));
2605
  error= (access(ds_filename.str, F_OK) != 0);
2606
  handle_command_error(command, error);
2607
  dynstr_free(&ds_filename);
2608
  DBUG_VOID_RETURN;
2609
}
2610
2611
2612
/*
2613
  SYNOPSIS
2614
  do_mkdir
2615
  command	called command
2616
2617
  DESCRIPTION
2618
  mkdir <dir_name>
2619
  Create the directory <dir_name>
2620
*/
2621
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2622
static void do_mkdir(struct st_command *command)
1 by brian
clean slate
2623
{
2624
  int error;
2625
  static DYNAMIC_STRING ds_dirname;
2626
  const struct command_arg mkdir_args[] = {
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2627
    {"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to create"}
1 by brian
clean slate
2628
  };
2629
  DBUG_ENTER("do_mkdir");
2630
2631
  check_command_args(command, command->first_argument,
2632
                     mkdir_args, sizeof(mkdir_args)/sizeof(struct command_arg),
2633
                     ' ');
2634
2635
  DBUG_PRINT("info", ("creating directory: %s", ds_dirname.str));
2636
  error= my_mkdir(ds_dirname.str, 0777, MYF(0)) != 0;
2637
  handle_command_error(command, error);
2638
  dynstr_free(&ds_dirname);
2639
  DBUG_VOID_RETURN;
2640
}
2641
2642
/*
2643
  SYNOPSIS
2644
  do_rmdir
2645
  command	called command
2646
2647
  DESCRIPTION
2648
  rmdir <dir_name>
2649
  Remove the empty directory <dir_name>
2650
*/
2651
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2652
static void do_rmdir(struct st_command *command)
1 by brian
clean slate
2653
{
2654
  int error;
2655
  static DYNAMIC_STRING ds_dirname;
2656
  const struct command_arg rmdir_args[] = {
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2657
    {"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to remove"}
1 by brian
clean slate
2658
  };
2659
  DBUG_ENTER("do_rmdir");
2660
2661
  check_command_args(command, command->first_argument,
2662
                     rmdir_args, sizeof(rmdir_args)/sizeof(struct command_arg),
2663
                     ' ');
2664
2665
  DBUG_PRINT("info", ("removing directory: %s", ds_dirname.str));
2666
  error= rmdir(ds_dirname.str) != 0;
2667
  handle_command_error(command, error);
2668
  dynstr_free(&ds_dirname);
2669
  DBUG_VOID_RETURN;
2670
}
2671
2672
2673
/*
2674
  Read characters from line buffer or file. This is needed to allow
2675
  my_ungetc() to buffer MAX_DELIMITER_LENGTH characters for a file
2676
2677
  NOTE:
2678
  This works as long as one doesn't change files (with 'source file_name')
2679
  when there is things pushed into the buffer.  This should however not
2680
  happen for any tests in the test suite.
2681
*/
2682
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2683
static int my_getc(FILE *file)
1 by brian
clean slate
2684
{
2685
  if (line_buffer_pos == line_buffer)
2686
    return fgetc(file);
2687
  return *--line_buffer_pos;
2688
}
2689
2690
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2691
static void my_ungetc(int c)
1 by brian
clean slate
2692
{
2693
  *line_buffer_pos++= (char) c;
2694
}
2695
2696
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2697
static void read_until_delimiter(DYNAMIC_STRING *ds,
2698
                                 DYNAMIC_STRING *ds_delimiter)
1 by brian
clean slate
2699
{
2700
  char c;
2701
  DBUG_ENTER("read_until_delimiter");
2702
  DBUG_PRINT("enter", ("delimiter: %s, length: %u",
2703
                       ds_delimiter->str, (uint) ds_delimiter->length));
2704
2705
  if (ds_delimiter->length > MAX_DELIMITER_LENGTH)
2706
    die("Max delimiter length(%d) exceeded", MAX_DELIMITER_LENGTH);
2707
2708
  /* Read from file until delimiter is found */
2709
  while (1)
2710
  {
2711
    c= my_getc(cur_file->file);
2712
2713
    if (c == '\n')
2714
    {
2715
      cur_file->lineno++;
2716
2717
      /* Skip newline from the same line as the command */
2718
      if (start_lineno == (cur_file->lineno - 1))
2719
        continue;
2720
    }
2721
    else if (start_lineno == cur_file->lineno)
2722
    {
2723
      /*
2724
        No characters except \n are allowed on
2725
        the same line as the command
2726
      */
2727
      die("Trailing characters found after command");
2728
    }
2729
2730
    if (feof(cur_file->file))
2731
      die("End of file encountered before '%s' delimiter was found",
2732
          ds_delimiter->str);
2733
2734
    if (match_delimiter(c, ds_delimiter->str, ds_delimiter->length))
2735
    {
2736
      DBUG_PRINT("exit", ("Found delimiter '%s'", ds_delimiter->str));
2737
      break;
2738
    }
2739
    dynstr_append_mem(ds, (const char*)&c, 1);
2740
  }
2741
  DBUG_PRINT("exit", ("ds: %s", ds->str));
2742
  DBUG_VOID_RETURN;
2743
}
2744
2745
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2746
static void do_write_file_command(struct st_command *command, my_bool append)
1 by brian
clean slate
2747
{
2748
  static DYNAMIC_STRING ds_content;
2749
  static DYNAMIC_STRING ds_filename;
2750
  static DYNAMIC_STRING ds_delimiter;
2751
  const struct command_arg write_file_args[] = {
2752
    { "filename", ARG_STRING, TRUE, &ds_filename, "File to write to" },
2753
    { "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" }
2754
  };
2755
  DBUG_ENTER("do_write_file");
2756
2757
  check_command_args(command,
2758
                     command->first_argument,
2759
                     write_file_args,
2760
                     sizeof(write_file_args)/sizeof(struct command_arg),
2761
                     ' ');
2762
2763
  /* If no delimiter was provided, use EOF */
2764
  if (ds_delimiter.length == 0)
2765
    dynstr_set(&ds_delimiter, "EOF");
2766
2767
  if (!append && access(ds_filename.str, F_OK) == 0)
2768
  {
2769
    /* The file should not be overwritten */
2770
    die("File already exist: '%s'", ds_filename.str);
2771
  }
2772
2773
  init_dynamic_string(&ds_content, "", 1024, 1024);
2774
  read_until_delimiter(&ds_content, &ds_delimiter);
2775
  DBUG_PRINT("info", ("Writing to file: %s", ds_filename.str));
2776
  str_to_file2(ds_filename.str, ds_content.str, ds_content.length, append);
2777
  dynstr_free(&ds_content);
2778
  dynstr_free(&ds_filename);
2779
  dynstr_free(&ds_delimiter);
2780
  DBUG_VOID_RETURN;
2781
}
2782
2783
2784
/*
2785
  SYNOPSIS
2786
  do_write_file
2787
  command	called command
2788
2789
  DESCRIPTION
2790
  write_file <file_name> [<delimiter>];
2791
  <what to write line 1>
2792
  <...>
2793
  < what to write line n>
2794
  EOF
2795
2796
  --write_file <file_name>;
2797
  <what to write line 1>
2798
  <...>
2799
  < what to write line n>
2800
  EOF
2801
2802
  Write everything between the "write_file" command and 'delimiter'
2803
  to "file_name"
2804
2805
  NOTE! Will fail if <file_name> exists
2806
2807
  Default <delimiter> is EOF
2808
2809
*/
2810
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2811
static void do_write_file(struct st_command *command)
1 by brian
clean slate
2812
{
2813
  do_write_file_command(command, FALSE);
2814
}
2815
2816
2817
/*
2818
  SYNOPSIS
2819
  do_append_file
2820
  command	called command
2821
2822
  DESCRIPTION
2823
  append_file <file_name> [<delimiter>];
2824
  <what to write line 1>
2825
  <...>
2826
  < what to write line n>
2827
  EOF
2828
2829
  --append_file <file_name>;
2830
  <what to write line 1>
2831
  <...>
2832
  < what to write line n>
2833
  EOF
2834
2835
  Append everything between the "append_file" command
2836
  and 'delimiter' to "file_name"
2837
2838
  Default <delimiter> is EOF
2839
2840
*/
2841
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2842
static void do_append_file(struct st_command *command)
1 by brian
clean slate
2843
{
2844
  do_write_file_command(command, TRUE);
2845
}
2846
2847
2848
/*
2849
  SYNOPSIS
2850
  do_cat_file
2851
  command	called command
2852
2853
  DESCRIPTION
2854
  cat_file <file_name>;
2855
2856
  Print the given file to result log
2857
2858
*/
2859
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2860
static void do_cat_file(struct st_command *command)
1 by brian
clean slate
2861
{
2862
  static DYNAMIC_STRING ds_filename;
2863
  const struct command_arg cat_file_args[] = {
2864
    { "filename", ARG_STRING, TRUE, &ds_filename, "File to read from" }
2865
  };
2866
  DBUG_ENTER("do_cat_file");
2867
2868
  check_command_args(command,
2869
                     command->first_argument,
2870
                     cat_file_args,
2871
                     sizeof(cat_file_args)/sizeof(struct command_arg),
2872
                     ' ');
2873
2874
  DBUG_PRINT("info", ("Reading from, file: %s", ds_filename.str));
2875
2876
  cat_file(&ds_res, ds_filename.str);
2877
2878
  dynstr_free(&ds_filename);
2879
  DBUG_VOID_RETURN;
2880
}
2881
2882
2883
/*
2884
  SYNOPSIS
2885
  do_diff_files
2886
  command	called command
2887
2888
  DESCRIPTION
2889
  diff_files <file1> <file2>;
2890
2891
  Fails if the two files differ.
2892
2893
*/
2894
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2895
static void do_diff_files(struct st_command *command)
1 by brian
clean slate
2896
{
2897
  int error= 0;
2898
  static DYNAMIC_STRING ds_filename;
2899
  static DYNAMIC_STRING ds_filename2;
2900
  const struct command_arg diff_file_args[] = {
2901
    { "file1", ARG_STRING, TRUE, &ds_filename, "First file to diff" },
2902
    { "file2", ARG_STRING, TRUE, &ds_filename2, "Second file to diff" }
2903
  };
2904
  DBUG_ENTER("do_diff_files");
2905
2906
  check_command_args(command,
2907
                     command->first_argument,
2908
                     diff_file_args,
2909
                     sizeof(diff_file_args)/sizeof(struct command_arg),
2910
                     ' ');
2911
2912
  if ((error= compare_files(ds_filename.str, ds_filename2.str)))
2913
  {
2914
    /* Compare of the two files failed, append them to output
2915
       so the failure can be analyzed
2916
    */
2917
    show_diff(&ds_res, ds_filename.str, ds_filename2.str);
2918
  }
2919
2920
  dynstr_free(&ds_filename);
2921
  dynstr_free(&ds_filename2);
2922
  handle_command_error(command, error);
2923
  DBUG_VOID_RETURN;
2924
}
2925
2926
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2927
static struct st_connection * find_connection_by_name(const char *name)
1 by brian
clean slate
2928
{
2929
  struct st_connection *con;
2930
  for (con= connections; con < next_con; con++)
2931
  {
2932
    if (!strcmp(con->name, name))
2933
    {
2934
      return con;
2935
    }
2936
  }
2937
  return 0; /* Connection not found */
2938
}
2939
2940
2941
/*
2942
  SYNOPSIS
2943
  do_send_quit
2944
  command	called command
2945
2946
  DESCRIPTION
2947
  Sends a simple quit command to the server for the named connection.
2948
2949
*/
2950
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2951
static void do_send_quit(struct st_command *command)
1 by brian
clean slate
2952
{
2953
  char *p= command->first_argument, *name;
2954
  struct st_connection *con;
2955
2956
  DBUG_ENTER("do_send_quit");
2957
  DBUG_PRINT("enter",("name: '%s'",p));
2958
2959
  if (!*p)
2960
    die("Missing connection name in send_quit");
2961
  name= p;
2962
  while (*p && !my_isspace(charset_info,*p))
2963
    p++;
2964
2965
  if (*p)
2966
    *p++= 0;
2967
  command->last_argument= p;
2968
2969
  if (!(con= find_connection_by_name(name)))
2970
    die("connection '%s' not found in connection pool", name);
2971
2972
  simple_command(&con->mysql,COM_QUIT,0,0,1);
2973
2974
  DBUG_VOID_RETURN;
2975
}
2976
2977
2978
/*
2979
  SYNOPSIS
2980
  do_change_user
2981
  command       called command
2982
2983
  DESCRIPTION
2984
  change_user [<user>], [<passwd>], [<db>]
2985
  <user> - user to change to
2986
  <passwd> - user password
2987
  <db> - default database
2988
2989
  Changes the user and causes the database specified by db to become
2990
  the default (current) database for the the current connection.
2991
2992
*/
2993
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
2994
static void do_change_user(struct st_command *command)
1 by brian
clean slate
2995
{
2996
  MYSQL *mysql = &cur_con->mysql;
2997
  /* static keyword to make the NetWare compiler happy. */
2998
  static DYNAMIC_STRING ds_user, ds_passwd, ds_db;
2999
  const struct command_arg change_user_args[] = {
3000
    { "user", ARG_STRING, FALSE, &ds_user, "User to connect as" },
3001
    { "password", ARG_STRING, FALSE, &ds_passwd, "Password used when connecting" },
3002
    { "database", ARG_STRING, FALSE, &ds_db, "Database to select after connect" },
3003
  };
3004
3005
  DBUG_ENTER("do_change_user");
3006
3007
  check_command_args(command, command->first_argument,
3008
                     change_user_args,
3009
                     sizeof(change_user_args)/sizeof(struct command_arg),
3010
                     ',');
3011
3012
  if (!ds_user.length)
3013
    dynstr_set(&ds_user, mysql->user);
3014
3015
  if (!ds_passwd.length)
3016
    dynstr_set(&ds_passwd, mysql->passwd);
3017
3018
  if (!ds_db.length)
3019
    dynstr_set(&ds_db, mysql->db);
3020
3021
  DBUG_PRINT("info",("connection: '%s' user: '%s' password: '%s' database: '%s'",
3022
                      cur_con->name, ds_user.str, ds_passwd.str, ds_db.str));
3023
3024
  if (mysql_change_user(mysql, ds_user.str, ds_passwd.str, ds_db.str))
3025
    die("change user failed: %s", mysql_error(mysql));
3026
3027
  dynstr_free(&ds_user);
3028
  dynstr_free(&ds_passwd);
3029
  dynstr_free(&ds_db);
3030
3031
  DBUG_VOID_RETURN;
3032
}
3033
3034
3035
/*
3036
  SYNOPSIS
3037
  do_perl
3038
  command	command handle
3039
3040
  DESCRIPTION
3041
  perl [<delimiter>];
3042
  <perlscript line 1>
3043
  <...>
3044
  <perlscript line n>
3045
  EOF
3046
3047
  Execute everything after "perl" until <delimiter> as perl.
3048
  Useful for doing more advanced things
3049
  but still being able to execute it on all platforms.
3050
3051
  Default <delimiter> is EOF
3052
*/
3053
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3054
static void do_perl(struct st_command *command)
1 by brian
clean slate
3055
{
3056
  int error;
3057
  File fd;
3058
  FILE *res_file;
3059
  char buf[FN_REFLEN];
3060
  char temp_file_path[FN_REFLEN];
3061
  static DYNAMIC_STRING ds_script;
3062
  static DYNAMIC_STRING ds_delimiter;
3063
  const struct command_arg perl_args[] = {
3064
    { "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" }
3065
  };
3066
  DBUG_ENTER("do_perl");
3067
3068
  check_command_args(command,
3069
                     command->first_argument,
3070
                     perl_args,
3071
                     sizeof(perl_args)/sizeof(struct command_arg),
3072
                     ' ');
3073
3074
  /* If no delimiter was provided, use EOF */
3075
  if (ds_delimiter.length == 0)
3076
    dynstr_set(&ds_delimiter, "EOF");
3077
3078
  init_dynamic_string(&ds_script, "", 1024, 1024);
3079
  read_until_delimiter(&ds_script, &ds_delimiter);
3080
3081
  DBUG_PRINT("info", ("Executing perl: %s", ds_script.str));
3082
3083
  /* Create temporary file name */
3084
  if ((fd= create_temp_file(temp_file_path, getenv("MYSQLTEST_VARDIR"),
3085
                            "tmp", O_CREAT | O_SHARE | O_RDWR,
3086
                            MYF(MY_WME))) < 0)
3087
    die("Failed to create temporary file for perl command");
3088
  my_close(fd, MYF(0));
3089
3090
  str_to_file(temp_file_path, ds_script.str, ds_script.length);
3091
3092
  /* Format the "perl <filename>" command */
77.1.18 by Monty Taylor
Removed my_vsnprintf and my_snprintf.
3093
  snprintf(buf, sizeof(buf), "perl %s", temp_file_path);
1 by brian
clean slate
3094
3095
  if (!(res_file= popen(buf, "r")) && command->abort_on_error)
3096
    die("popen(\"%s\", \"r\") failed", buf);
3097
3098
  while (fgets(buf, sizeof(buf), res_file))
3099
  {
3100
    if (disable_result_log)
3101
    {
3102
      buf[strlen(buf)-1]=0;
3103
      DBUG_PRINT("exec_result",("%s", buf));
3104
    }
3105
    else
3106
    {
3107
      replace_dynstr_append(&ds_res, buf);
3108
    }
3109
  }
3110
  error= pclose(res_file);
3111
3112
  /* Remove the temporary file */
3113
  my_delete(temp_file_path, MYF(0));
3114
3115
  handle_command_error(command, WEXITSTATUS(error));
3116
  dynstr_free(&ds_script);
3117
  dynstr_free(&ds_delimiter);
3118
  DBUG_VOID_RETURN;
3119
}
3120
3121
3122
/*
3123
  Print the content between echo and <delimiter> to result file.
3124
  Evaluate all variables in the string before printing, allow
3125
  for variable names to be escaped using \
3126
3127
  SYNOPSIS
3128
  do_echo()
3129
  command  called command
3130
3131
  DESCRIPTION
3132
  echo text
3133
  Print the text after echo until end of command to result file
3134
3135
  echo $<var_name>
3136
  Print the content of the variable <var_name> to result file
3137
3138
  echo Some text $<var_name>
3139
  Print "Some text" plus the content of the variable <var_name> to
3140
  result file
3141
3142
  echo Some text \$<var_name>
3143
  Print "Some text" plus $<var_name> to result file
3144
*/
3145
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3146
static int do_echo(struct st_command *command)
1 by brian
clean slate
3147
{
3148
  DYNAMIC_STRING ds_echo;
3149
  DBUG_ENTER("do_echo");
3150
3151
  init_dynamic_string(&ds_echo, "", command->query_len, 256);
3152
  do_eval(&ds_echo, command->first_argument, command->end, FALSE);
3153
  dynstr_append_mem(&ds_res, ds_echo.str, ds_echo.length);
3154
  dynstr_append_mem(&ds_res, "\n", 1);
3155
  dynstr_free(&ds_echo);
3156
  command->last_argument= command->end;
3157
  DBUG_RETURN(0);
3158
}
3159
3160
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3161
static void
3162
do_wait_for_slave_to_stop(struct st_command *c __attribute__((unused)))
1 by brian
clean slate
3163
{
3164
  static int SLAVE_POLL_INTERVAL= 300000;
3165
  MYSQL* mysql = &cur_con->mysql;
3166
  for (;;)
3167
  {
3168
    MYSQL_RES *res= NULL;
3169
    MYSQL_ROW row;
3170
    int done;
3171
3172
    if (mysql_query(mysql,"show status like 'Slave_running'") ||
3173
	!(res=mysql_store_result(mysql)))
3174
      die("Query failed while probing slave for stop: %s",
3175
	  mysql_error(mysql));
3176
    if (!(row=mysql_fetch_row(res)) || !row[1])
3177
    {
3178
      mysql_free_result(res);
3179
      die("Strange result from query while probing slave for stop");
3180
    }
3181
    done = !strcmp(row[1],"OFF");
3182
    mysql_free_result(res);
3183
    if (done)
3184
      break;
3185
    my_sleep(SLAVE_POLL_INTERVAL);
3186
  }
3187
  return;
3188
}
3189
3190
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3191
static void do_sync_with_master2(long offset)
1 by brian
clean slate
3192
{
3193
  MYSQL_RES *res;
3194
  MYSQL_ROW row;
3195
  MYSQL *mysql= &cur_con->mysql;
3196
  char query_buf[FN_REFLEN+128];
3197
  int tries= 0;
3198
3199
  if (!master_pos.file[0])
3200
    die("Calling 'sync_with_master' without calling 'save_master_pos'");
3201
3202
  sprintf(query_buf, "select master_pos_wait('%s', %ld)", master_pos.file,
3203
	  master_pos.pos + offset);
3204
3205
wait_for_position:
3206
3207
  if (mysql_query(mysql, query_buf))
3208
    die("failed in '%s': %d: %s", query_buf, mysql_errno(mysql),
3209
        mysql_error(mysql));
3210
3211
  if (!(res= mysql_store_result(mysql)))
3212
    die("mysql_store_result() returned NULL for '%s'", query_buf);
3213
  if (!(row= mysql_fetch_row(res)))
3214
  {
3215
    mysql_free_result(res);
3216
    die("empty result in %s", query_buf);
3217
  }
3218
  if (!row[0])
3219
  {
3220
    /*
3221
      It may be that the slave SQL thread has not started yet, though START
3222
      SLAVE has been issued ?
3223
    */
3224
    mysql_free_result(res);
3225
    if (tries++ == 30)
3226
    {
3227
      show_query(mysql, "SHOW MASTER STATUS");
3228
      show_query(mysql, "SHOW SLAVE STATUS");
3229
      die("could not sync with master ('%s' returned NULL)", query_buf);
3230
    }
3231
    sleep(1); /* So at most we will wait 30 seconds and make 31 tries */
3232
    goto wait_for_position;
3233
  }
3234
  mysql_free_result(res);
3235
  return;
3236
}
3237
3238
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3239
static void do_sync_with_master(struct st_command *command)
1 by brian
clean slate
3240
{
3241
  long offset= 0;
3242
  char *p= command->first_argument;
3243
  const char *offset_start= p;
3244
  if (*offset_start)
3245
  {
3246
    for (; my_isdigit(charset_info, *p); p++)
3247
      offset = offset * 10 + *p - '0';
3248
3249
    if(*p && !my_isspace(charset_info, *p))
3250
      die("Invalid integer argument \"%s\"", offset_start);
3251
    command->last_argument= p;
3252
  }
3253
  do_sync_with_master2(offset);
3254
  return;
3255
}
3256
3257
3258
/*
3259
  when ndb binlog is on, this call will wait until last updated epoch
3260
  (locally in the mysqld) has been received into the binlog
3261
*/
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3262
static int do_save_master_pos(void)
1 by brian
clean slate
3263
{
3264
  MYSQL_RES *res;
3265
  MYSQL_ROW row;
3266
  MYSQL *mysql = &cur_con->mysql;
3267
  const char *query;
3268
  DBUG_ENTER("do_save_master_pos");
3269
3270
#ifdef HAVE_NDB_BINLOG
3271
  /*
3272
    Wait for ndb binlog to be up-to-date with all changes
3273
    done on the local mysql server
3274
  */
3275
  {
3276
    ulong have_ndbcluster;
3277
    if (mysql_query(mysql, query= "show variables like 'have_ndbcluster'"))
3278
      die("'%s' failed: %d %s", query,
3279
          mysql_errno(mysql), mysql_error(mysql));
3280
    if (!(res= mysql_store_result(mysql)))
3281
      die("mysql_store_result() returned NULL for '%s'", query);
3282
    if (!(row= mysql_fetch_row(res)))
3283
      die("Query '%s' returned empty result", query);
3284
3285
    have_ndbcluster= strcmp("YES", row[1]) == 0;
3286
    mysql_free_result(res);
3287
3288
    if (have_ndbcluster)
3289
    {
3290
      ulonglong start_epoch= 0, handled_epoch= 0,
3291
	latest_epoch=0, latest_trans_epoch=0,
3292
	latest_handled_binlog_epoch= 0, latest_received_binlog_epoch= 0,
3293
	latest_applied_binlog_epoch= 0;
3294
      int count= 0;
3295
      int do_continue= 1;
3296
      while (do_continue)
3297
      {
3298
        const char binlog[]= "binlog";
3299
	const char latest_epoch_str[]=
3300
          "latest_epoch=";
3301
        const char latest_trans_epoch_str[]=
3302
          "latest_trans_epoch=";
3303
	const char latest_received_binlog_epoch_str[]=
3304
	  "latest_received_binlog_epoch";
3305
        const char latest_handled_binlog_epoch_str[]=
3306
          "latest_handled_binlog_epoch=";
3307
        const char latest_applied_binlog_epoch_str[]=
3308
          "latest_applied_binlog_epoch=";
3309
        if (count)
3310
          sleep(1);
3311
        if (mysql_query(mysql, query= "show engine ndb status"))
3312
          die("failed in '%s': %d %s", query,
3313
              mysql_errno(mysql), mysql_error(mysql));
3314
        if (!(res= mysql_store_result(mysql)))
3315
          die("mysql_store_result() returned NULL for '%s'", query);
3316
        while ((row= mysql_fetch_row(res)))
3317
        {
3318
          if (strcmp(row[1], binlog) == 0)
3319
          {
3320
            const char *status= row[2];
3321
3322
	    /* latest_epoch */
3323
	    while (*status && strncmp(status, latest_epoch_str,
3324
				      sizeof(latest_epoch_str)-1))
3325
	      status++;
3326
	    if (*status)
3327
            {
3328
	      status+= sizeof(latest_epoch_str)-1;
3329
	      latest_epoch= strtoull(status, (char**) 0, 10);
3330
	    }
3331
	    else
3332
	      die("result does not contain '%s' in '%s'",
3333
		  latest_epoch_str, query);
3334
	    /* latest_trans_epoch */
3335
	    while (*status && strncmp(status, latest_trans_epoch_str,
3336
				      sizeof(latest_trans_epoch_str)-1))
3337
	      status++;
3338
	    if (*status)
3339
	    {
3340
	      status+= sizeof(latest_trans_epoch_str)-1;
3341
	      latest_trans_epoch= strtoull(status, (char**) 0, 10);
3342
	    }
3343
	    else
3344
	      die("result does not contain '%s' in '%s'",
3345
		  latest_trans_epoch_str, query);
3346
	    /* latest_received_binlog_epoch */
3347
	    while (*status &&
3348
		   strncmp(status, latest_received_binlog_epoch_str,
3349
			   sizeof(latest_received_binlog_epoch_str)-1))
3350
	      status++;
3351
	    if (*status)
3352
	    {
3353
	      status+= sizeof(latest_received_binlog_epoch_str)-1;
3354
	      latest_received_binlog_epoch= strtoull(status, (char**) 0, 10);
3355
	    }
3356
	    else
3357
	      die("result does not contain '%s' in '%s'",
3358
		  latest_received_binlog_epoch_str, query);
3359
	    /* latest_handled_binlog */
3360
	    while (*status &&
3361
		   strncmp(status, latest_handled_binlog_epoch_str,
3362
			   sizeof(latest_handled_binlog_epoch_str)-1))
3363
	      status++;
3364
	    if (*status)
3365
	    {
3366
	      status+= sizeof(latest_handled_binlog_epoch_str)-1;
3367
	      latest_handled_binlog_epoch= strtoull(status, (char**) 0, 10);
3368
	    }
3369
	    else
3370
	      die("result does not contain '%s' in '%s'",
3371
		  latest_handled_binlog_epoch_str, query);
3372
	    /* latest_applied_binlog_epoch */
3373
	    while (*status &&
3374
		   strncmp(status, latest_applied_binlog_epoch_str,
3375
			   sizeof(latest_applied_binlog_epoch_str)-1))
3376
	      status++;
3377
	    if (*status)
3378
	    {
3379
	      status+= sizeof(latest_applied_binlog_epoch_str)-1;
3380
	      latest_applied_binlog_epoch= strtoull(status, (char**) 0, 10);
3381
	    }
3382
	    else
3383
	      die("result does not contain '%s' in '%s'",
3384
		  latest_applied_binlog_epoch_str, query);
3385
	    if (count == 0)
3386
	      start_epoch= latest_trans_epoch;
3387
	    break;
3388
	  }
3389
	}
3390
	if (!row)
3391
	  die("result does not contain '%s' in '%s'",
3392
	      binlog, query);
3393
	if (latest_handled_binlog_epoch > handled_epoch)
3394
	  count= 0;
3395
	handled_epoch= latest_handled_binlog_epoch;
3396
	count++;
3397
	if (latest_handled_binlog_epoch >= start_epoch)
3398
          do_continue= 0;
3399
        else if (count > 30)
3400
	{
3401
	  break;
3402
        }
3403
        mysql_free_result(res);
3404
      }
3405
    }
3406
  }
3407
#endif
3408
  if (mysql_query(mysql, query= "show master status"))
3409
    die("failed in 'show master status': %d %s",
3410
	mysql_errno(mysql), mysql_error(mysql));
3411
3412
  if (!(res = mysql_store_result(mysql)))
3413
    die("mysql_store_result() retuned NULL for '%s'", query);
3414
  if (!(row = mysql_fetch_row(res)))
3415
    die("empty result in show master status");
3416
  strnmov(master_pos.file, row[0], sizeof(master_pos.file)-1);
3417
  master_pos.pos = strtoul(row[1], (char**) 0, 10);
3418
  mysql_free_result(res);
3419
  DBUG_RETURN(0);
3420
}
3421
3422
3423
/*
3424
  Assign the variable <var_name> with <var_val>
3425
3426
  SYNOPSIS
3427
  do_let()
3428
  query	called command
3429
3430
  DESCRIPTION
3431
  let $<var_name>=<var_val><delimiter>
3432
3433
  <var_name>  - is the string string found between the $ and =
3434
  <var_val>   - is the content between the = and <delimiter>, it may span
3435
  multiple line and contain any characters except <delimiter>
3436
  <delimiter> - is a string containing of one or more chars, default is ;
3437
3438
  RETURN VALUES
3439
  Program will die if error detected
3440
*/
3441
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3442
static void do_let(struct st_command *command)
1 by brian
clean slate
3443
{
3444
  char *p= command->first_argument;
3445
  char *var_name, *var_name_end;
3446
  DYNAMIC_STRING let_rhs_expr;
3447
  DBUG_ENTER("do_let");
3448
3449
  init_dynamic_string(&let_rhs_expr, "", 512, 2048);
3450
3451
  /* Find <var_name> */
3452
  if (!*p)
3453
    die("Missing arguments to let");
3454
  var_name= p;
3455
  while (*p && (*p != '=') && !my_isspace(charset_info,*p))
3456
    p++;
3457
  var_name_end= p;
3458
  if (var_name == var_name_end ||
3459
      (var_name+1 == var_name_end && *var_name == '$'))
3460
    die("Missing variable name in let");
3461
  while (my_isspace(charset_info,*p))
3462
    p++;
3463
  if (*p++ != '=')
3464
    die("Missing assignment operator in let");
3465
3466
  /* Find start of <var_val> */
3467
  while (*p && my_isspace(charset_info,*p))
3468
    p++;
3469
3470
  do_eval(&let_rhs_expr, p, command->end, FALSE);
3471
3472
  command->last_argument= command->end;
3473
  /* Assign var_val to var_name */
3474
  var_set(var_name, var_name_end, let_rhs_expr.str,
3475
          (let_rhs_expr.str + let_rhs_expr.length));
3476
  dynstr_free(&let_rhs_expr);
3477
  DBUG_VOID_RETURN;
3478
}
3479
3480
3481
/*
3482
  Sleep the number of specified seconds
3483
3484
  SYNOPSIS
3485
  do_sleep()
3486
  q	       called command
3487
  real_sleep   use the value from opt_sleep as number of seconds to sleep
3488
               if real_sleep is false
3489
3490
  DESCRIPTION
3491
  sleep <seconds>
3492
  real_sleep <seconds>
3493
3494
  The difference between the sleep and real_sleep commands is that sleep
3495
  uses the delay from the --sleep command-line option if there is one.
3496
  (If the --sleep option is not given, the sleep command uses the delay
3497
  specified by its argument.) The real_sleep command always uses the
3498
  delay specified by its argument.  The logic is that sometimes delays are
3499
  cpu-dependent, and --sleep can be used to set this delay.  real_sleep is
3500
  used for cpu-independent delays.
3501
*/
3502
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3503
static int do_sleep(struct st_command *command, my_bool real_sleep)
1 by brian
clean slate
3504
{
3505
  int error= 0;
3506
  char *p= command->first_argument;
3507
  char *sleep_start, *sleep_end= command->end;
3508
  double sleep_val;
3509
3510
  while (my_isspace(charset_info, *p))
3511
    p++;
3512
  if (!*p)
3513
    die("Missing argument to %.*s", command->first_word_len, command->query);
3514
  sleep_start= p;
3515
  /* Check that arg starts with a digit, not handled by my_strtod */
3516
  if (!my_isdigit(charset_info, *sleep_start))
3517
    die("Invalid argument to %.*s \"%s\"", command->first_word_len,
3518
        command->query,command->first_argument);
3519
  sleep_val= my_strtod(sleep_start, &sleep_end, &error);
3520
  if (error)
3521
    die("Invalid argument to %.*s \"%s\"", command->first_word_len,
3522
        command->query, command->first_argument);
3523
3524
  /* Fixed sleep time selected by --sleep option */
3525
  if (opt_sleep >= 0 && !real_sleep)
3526
    sleep_val= opt_sleep;
3527
3528
  DBUG_PRINT("info", ("sleep_val: %f", sleep_val));
3529
  if (sleep_val)
3530
    my_sleep((ulong) (sleep_val * 1000000L));
3531
  command->last_argument= sleep_end;
3532
  return 0;
3533
}
3534
3535
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3536
static void do_get_file_name(struct st_command *command,
1 by brian
clean slate
3537
                      char* dest, uint dest_max_len)
3538
{
3539
  char *p= command->first_argument, *name;
3540
  if (!*p)
3541
    die("Missing file name argument");
3542
  name= p;
3543
  while (*p && !my_isspace(charset_info,*p))
3544
    p++;
3545
  if (*p)
3546
    *p++= 0;
3547
  command->last_argument= p;
3548
  strmake(dest, name, dest_max_len - 1);
3549
}
3550
3551
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3552
static void do_set_charset(struct st_command *command)
1 by brian
clean slate
3553
{
3554
  char *charset_name= command->first_argument;
3555
  char *p;
3556
3557
  if (!charset_name || !*charset_name)
3558
    die("Missing charset name in 'character_set'");
3559
  /* Remove end space */
3560
  p= charset_name;
3561
  while (*p && !my_isspace(charset_info,*p))
3562
    p++;
3563
  if(*p)
3564
    *p++= 0;
3565
  command->last_argument= p;
3566
  charset_info= get_charset_by_csname(charset_name,MY_CS_PRIMARY,MYF(MY_WME));
3567
  if (!charset_info)
3568
    abort_not_supported_test("Test requires charset '%s'", charset_name);
3569
}
3570
3571
3572
#if MYSQL_VERSION_ID >= 50000
3573
/* List of error names to error codes, available from 5.0 */
3574
typedef struct
3575
{
3576
  const char *name;
3577
  uint        code;
3578
} st_error;
3579
3580
static st_error global_error_names[] =
3581
{
3582
#include <mysqld_ername.h>
3583
  { 0, 0 }
3584
};
3585
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3586
static uint get_errcode_from_name(char *error_name, char *error_end)
1 by brian
clean slate
3587
{
3588
  /* SQL error as string */
3589
  st_error *e= global_error_names;
3590
3591
  DBUG_ENTER("get_errcode_from_name");
3592
  DBUG_PRINT("enter", ("error_name: %s", error_name));
3593
3594
  /* Loop through the array of known error names */
3595
  for (; e->name; e++)
3596
  {
3597
    /*
3598
      If we get a match, we need to check the length of the name we
3599
      matched against in case it was longer than what we are checking
3600
      (as in ER_WRONG_VALUE vs. ER_WRONG_VALUE_COUNT).
3601
    */
3602
    if (!strncmp(error_name, e->name, (int) (error_end - error_name)) &&
3603
        (uint) strlen(e->name) == (uint) (error_end - error_name))
3604
    {
3605
      DBUG_RETURN(e->code);
3606
    }
3607
  }
3608
  if (!e->name)
3609
    die("Unknown SQL error name '%s'", error_name);
3610
  DBUG_RETURN(0);
3611
}
3612
#else
3613
uint get_errcode_from_name(char *error_name __attribute__((unused)),
3614
                           char *error_end __attribute__((unused)))
3615
{
3616
  abort_not_in_this_version();
3617
  return 0; /* Never reached */
3618
}
3619
#endif
3620
3621
3622
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3623
static void do_get_errcodes(struct st_command *command)
1 by brian
clean slate
3624
{
3625
  struct st_match_err *to= saved_expected_errors.err;
3626
  char *p= command->first_argument;
3627
  uint count= 0;
3628
3629
  DBUG_ENTER("do_get_errcodes");
3630
3631
  if (!*p)
3632
    die("Missing argument(s) to 'error'");
3633
3634
  do
3635
  {
3636
    char *end;
3637
3638
    /* Skip leading spaces */
3639
    while (*p && *p == ' ')
3640
      p++;
3641
3642
    /* Find end */
3643
    end= p;
3644
    while (*end && *end != ',' && *end != ' ')
3645
      end++;
3646
3647
    if (*p == 'S')
3648
    {
3649
      char *to_ptr= to->code.sqlstate;
3650
3651
      /*
3652
        SQLSTATE string
3653
        - Must be SQLSTATE_LENGTH long
3654
        - May contain only digits[0-9] and _uppercase_ letters
3655
      */
3656
      p++; /* Step past the S */
3657
      if ((end - p) != SQLSTATE_LENGTH)
3658
        die("The sqlstate must be exactly %d chars long", SQLSTATE_LENGTH);
3659
3660
      /* Check sqlstate string validity */
3661
      while (*p && p < end)
3662
      {
3663
        if (my_isdigit(charset_info, *p) || my_isupper(charset_info, *p))
3664
          *to_ptr++= *p++;
3665
        else
3666
          die("The sqlstate may only consist of digits[0-9] " \
3667
              "and _uppercase_ letters");
3668
      }
3669
3670
      *to_ptr= 0;
3671
      to->type= ERR_SQLSTATE;
3672
      DBUG_PRINT("info", ("ERR_SQLSTATE: %s", to->code.sqlstate));
3673
    }
3674
    else if (*p == 's')
3675
    {
3676
      die("The sqlstate definition must start with an uppercase S");
3677
    }
3678
    else if (*p == 'E')
3679
    {
3680
      /* Error name string */
3681
3682
      DBUG_PRINT("info", ("Error name: %s", p));
3683
      to->code.errnum= get_errcode_from_name(p, end);
3684
      to->type= ERR_ERRNO;
3685
      DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum));
3686
    }
3687
    else if (*p == 'e')
3688
    {
3689
      die("The error name definition must start with an uppercase E");
3690
    }
3691
    else
3692
    {
3693
      long val;
3694
      char *start= p;
3695
      /* Check that the string passed to str2int only contain digits */
3696
      while (*p && p != end)
3697
      {
3698
        if (!my_isdigit(charset_info, *p))
3699
          die("Invalid argument to error: '%s' - "\
3700
              "the errno may only consist of digits[0-9]",
3701
              command->first_argument);
3702
        p++;
3703
      }
3704
3705
      /* Convert the sting to int */
3706
      if (!str2int(start, 10, (long) INT_MIN, (long) INT_MAX, &val))
3707
	die("Invalid argument to error: '%s'", command->first_argument);
3708
3709
      to->code.errnum= (uint) val;
3710
      to->type= ERR_ERRNO;
3711
      DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum));
3712
    }
3713
    to++;
3714
    count++;
3715
3716
    if (count >= (sizeof(saved_expected_errors.err) /
3717
                  sizeof(struct st_match_err)))
3718
      die("Too many errorcodes specified");
3719
3720
    /* Set pointer to the end of the last error code */
3721
    p= end;
3722
3723
    /* Find next ',' */
3724
    while (*p && *p != ',')
3725
      p++;
3726
3727
    if (*p)
3728
      p++; /* Step past ',' */
3729
3730
  } while (*p);
3731
3732
  command->last_argument= p;
3733
  to->type= ERR_EMPTY;                        /* End of data */
3734
3735
  DBUG_PRINT("info", ("Expected errors: %d", count));
3736
  saved_expected_errors.count= count;
3737
  DBUG_VOID_RETURN;
3738
}
3739
3740
3741
/*
3742
  Get a string;  Return ptr to end of string
3743
  Strings may be surrounded by " or '
3744
3745
  If string is a '$variable', return the value of the variable.
3746
*/
3747
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3748
static char *get_string(char **to_ptr, char **from_ptr,
3749
                        struct st_command *command)
1 by brian
clean slate
3750
{
3751
  char c, sep;
3752
  char *to= *to_ptr, *from= *from_ptr, *start=to;
3753
  DBUG_ENTER("get_string");
3754
3755
  /* Find separator */
3756
  if (*from == '"' || *from == '\'')
3757
    sep= *from++;
3758
  else
3759
    sep=' ';				/* Separated with space */
3760
3761
  for ( ; (c=*from) ; from++)
3762
  {
3763
    if (c == '\\' && from[1])
3764
    {					/* Escaped character */
3765
      /* We can't translate \0 -> ASCII 0 as replace can't handle ASCII 0 */
3766
      switch (*++from) {
3767
      case 'n':
3768
	*to++= '\n';
3769
	break;
3770
      case 't':
3771
	*to++= '\t';
3772
	break;
3773
      case 'r':
3774
	*to++ = '\r';
3775
	break;
3776
      case 'b':
3777
	*to++ = '\b';
3778
	break;
3779
      case 'Z':				/* ^Z must be escaped on Win32 */
3780
	*to++='\032';
3781
	break;
3782
      default:
3783
	*to++ = *from;
3784
	break;
3785
      }
3786
    }
3787
    else if (c == sep)
3788
    {
3789
      if (c == ' ' || c != *++from)
3790
	break;				/* Found end of string */
3791
      *to++=c;				/* Copy duplicated separator */
3792
    }
3793
    else
3794
      *to++=c;
3795
  }
3796
  if (*from != ' ' && *from)
3797
    die("Wrong string argument in %s", command->query);
3798
3799
  while (my_isspace(charset_info,*from))	/* Point to next string */
3800
    from++;
3801
3802
  *to =0;				/* End of string marker */
3803
  *to_ptr= to+1;			/* Store pointer to end */
3804
  *from_ptr= from;
3805
3806
  /* Check if this was a variable */
3807
  if (*start == '$')
3808
  {
3809
    const char *end= to;
3810
    VAR *var=var_get(start, &end, 0, 1);
3811
    if (var && to == (char*) end+1)
3812
    {
3813
      DBUG_PRINT("info",("var: '%s' -> '%s'", start, var->str_val));
3814
      DBUG_RETURN(var->str_val);	/* return found variable value */
3815
    }
3816
  }
3817
  DBUG_RETURN(start);
3818
}
3819
3820
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3821
static void set_reconnect(MYSQL* mysql, int val)
1 by brian
clean slate
3822
{
3823
  my_bool reconnect= val;
3824
  DBUG_ENTER("set_reconnect");
3825
  DBUG_PRINT("info", ("val: %d", val));
3826
#if MYSQL_VERSION_ID < 50000
3827
  mysql->reconnect= reconnect;
3828
#else
3829
  mysql_options(mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
3830
#endif
3831
  DBUG_VOID_RETURN;
3832
}
3833
3834
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3835
static int select_connection_name(const char *name)
1 by brian
clean slate
3836
{
3837
  DBUG_ENTER("select_connection_name");
3838
  DBUG_PRINT("enter",("name: '%s'", name));
3839
3840
  if (!(cur_con= find_connection_by_name(name)))
3841
    die("connection '%s' not found in connection pool", name);
3842
3843
  /* Update $mysql_get_server_version to that of current connection */
3844
  var_set_mysql_get_server_version(&cur_con->mysql);
3845
3846
  DBUG_RETURN(0);
3847
}
3848
3849
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3850
static int select_connection(struct st_command *command)
1 by brian
clean slate
3851
{
3852
  char *name;
3853
  char *p= command->first_argument;
3854
  DBUG_ENTER("select_connection");
3855
3856
  if (!*p)
3857
    die("Missing connection name in connect");
3858
  name= p;
3859
  while (*p && !my_isspace(charset_info,*p))
3860
    p++;
3861
  if (*p)
3862
    *p++= 0;
3863
  command->last_argument= p;
3864
  DBUG_RETURN(select_connection_name(name));
3865
}
3866
3867
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3868
static void do_close_connection(struct st_command *command)
1 by brian
clean slate
3869
{
3870
  char *p= command->first_argument, *name;
3871
  struct st_connection *con;
3872
3873
  DBUG_ENTER("close_connection");
3874
  DBUG_PRINT("enter",("name: '%s'",p));
3875
3876
  if (!*p)
3877
    die("Missing connection name in disconnect");
3878
  name= p;
3879
  while (*p && !my_isspace(charset_info,*p))
3880
    p++;
3881
3882
  if (*p)
3883
    *p++= 0;
3884
  command->last_argument= p;
3885
3886
  if (!(con= find_connection_by_name(name)))
3887
    die("connection '%s' not found in connection pool", name);
3888
3889
  DBUG_PRINT("info", ("Closing connection %s", con->name));
3890
  if (command->type == Q_DIRTY_CLOSE)
3891
  {
3892
    if (con->mysql.net.vio)
3893
    {
3894
      vio_delete(con->mysql.net.vio);
3895
      con->mysql.net.vio = 0;
3896
    }
3897
  }
3898
3899
  mysql_close(&con->mysql);
3900
3901
  if (con->util_mysql)
3902
    mysql_close(con->util_mysql);
3903
  con->util_mysql= 0;
3904
3905
  my_free(con->name, MYF(0));
3906
3907
  /*
3908
    When the connection is closed set name to "-closed_connection-"
3909
    to make it possible to reuse the connection name.
3910
  */
3911
  if (!(con->name = my_strdup("-closed_connection-", MYF(MY_WME))))
3912
    die("Out of memory");
3913
3914
  DBUG_VOID_RETURN;
3915
}
3916
3917
3918
/*
3919
  Connect to a server doing several retries if needed.
3920
3921
  SYNOPSIS
3922
  safe_connect()
3923
  con               - connection structure to be used
3924
  host, user, pass, - connection parameters
3925
  db, port, sock
3926
3927
  NOTE
3928
3929
  Sometimes in a test the client starts before
3930
  the server - to solve the problem, we try again
3931
  after some sleep if connection fails the first
3932
  time
3933
3934
  This function will try to connect to the given server
3935
  "opt_max_connect_retries" times and sleep "connection_retry_sleep"
3936
  seconds between attempts before finally giving up.
3937
  This helps in situation when the client starts
3938
  before the server (which happens sometimes).
3939
  It will only ignore connection errors during these retries.
3940
3941
*/
3942
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
3943
static void safe_connect(MYSQL* mysql, const char *name, const char *host,
1 by brian
clean slate
3944
                  const char *user, const char *pass, const char *db,
3945
                  int port)
3946
{
3947
  int failed_attempts= 0;
3948
  static ulong connection_retry_sleep= 100000; /* Microseconds */
3949
3950
  DBUG_ENTER("safe_connect");
3951
  while(!mysql_real_connect(mysql, host, user, pass, db, port, NULL,
3952
                            CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS))
3953
  {
3954
    /*
3955
      Connect failed
3956
3957
      Only allow retry if this was an error indicating the server
3958
      could not be contacted. Error code differs depending
3959
      on protocol/connection type
3960
    */
3961
3962
    if ((mysql_errno(mysql) == CR_CONN_HOST_ERROR ||
3963
         mysql_errno(mysql) == CR_CONNECTION_ERROR) &&
3964
        failed_attempts < opt_max_connect_retries)
3965
    {
3966
      verbose_msg("Connect attempt %d/%d failed: %d: %s", failed_attempts,
3967
                  opt_max_connect_retries, mysql_errno(mysql),
3968
                  mysql_error(mysql));
3969
      my_sleep(connection_retry_sleep);
3970
    }
3971
    else
3972
    {
3973
      if (failed_attempts > 0)
3974
        die("Could not open connection '%s' after %d attempts: %d %s", name,
3975
            failed_attempts, mysql_errno(mysql), mysql_error(mysql));
3976
      else
3977
        die("Could not open connection '%s': %d %s", name,
3978
            mysql_errno(mysql), mysql_error(mysql));
3979
    }
3980
    failed_attempts++;
3981
  }
3982
  DBUG_VOID_RETURN;
3983
}
3984
3985
3986
/*
3987
  Connect to a server and handle connection errors in case they occur.
3988
3989
  SYNOPSIS
3990
  connect_n_handle_errors()
3991
  q                 - context of connect "query" (command)
3992
  con               - connection structure to be used
3993
  host, user, pass, - connection parameters
3994
  db, port, sock
3995
3996
  DESCRIPTION
3997
  This function will try to establish a connection to server and handle
3998
  possible errors in the same manner as if "connect" was usual SQL-statement
3999
  (If error is expected it will ignore it once it occurs and log the
4000
  "statement" to the query log).
4001
  Unlike safe_connect() it won't do several attempts.
4002
4003
  RETURN VALUES
4004
  1 - Connected
4005
  0 - Not connected
4006
4007
*/
4008
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
4009
static int connect_n_handle_errors(struct st_command *command,
1 by brian
clean slate
4010
                            MYSQL* con, const char* host,
4011
                            const char* user, const char* pass,
4012
                            const char* db, int port, const char* sock)
4013
{
4014
  DYNAMIC_STRING *ds;
4015
4016
  ds= &ds_res;
4017
4018
  /* Only log if an error is expected */
4019
  if (!command->abort_on_error &&
4020
      !disable_query_log)
4021
  {
4022
    /*
4023
      Log the connect to result log
4024
    */
4025
    dynstr_append_mem(ds, "connect(", 8);
4026
    replace_dynstr_append(ds, host);
4027
    dynstr_append_mem(ds, ",", 1);
4028
    replace_dynstr_append(ds, user);
4029
    dynstr_append_mem(ds, ",", 1);
4030
    replace_dynstr_append(ds, pass);
4031
    dynstr_append_mem(ds, ",", 1);
4032
    if (db)
4033
      replace_dynstr_append(ds, db);
4034
    dynstr_append_mem(ds, ",", 1);
4035
    replace_dynstr_append_uint(ds, port);
4036
    dynstr_append_mem(ds, ",", 1);
4037
    if (sock)
4038
      replace_dynstr_append(ds, sock);
4039
    dynstr_append_mem(ds, ")", 1);
4040
    dynstr_append_mem(ds, delimiter, delimiter_length);
4041
    dynstr_append_mem(ds, "\n", 1);
4042
  }
4043
  if (!mysql_real_connect(con, host, user, pass, db, port, 0,
4044
                          CLIENT_MULTI_STATEMENTS))
4045
  {
4046
    var_set_errno(mysql_errno(con));
4047
    handle_error(command, mysql_errno(con), mysql_error(con),
4048
		 mysql_sqlstate(con), ds);
4049
    return 0; /* Not connected */
4050
  }
4051
4052
  var_set_errno(0);
4053
  handle_no_error(command);
4054
  return 1; /* Connected */
4055
}
4056
4057
4058
/*
4059
  Open a new connection to MySQL Server with the parameters
4060
  specified. Make the new connection the current connection.
4061
4062
  SYNOPSIS
4063
  do_connect()
4064
  q	       called command
4065
4066
  DESCRIPTION
4067
  connect(<name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]]);
4068
  connect <name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]];
4069
4070
  <name> - name of the new connection
4071
  <host> - hostname of server
4072
  <user> - user to connect as
4073
  <pass> - password used when connecting
4074
  <db>   - initial db when connected
4075
  <port> - server port
4076
  <sock> - server socket
4077
  <opts> - options to use for the connection
4078
   * SSL - use SSL if available
4079
   * COMPRESS - use compression if available
4080
4081
*/
4082
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
4083
static void do_connect(struct st_command *command)
1 by brian
clean slate
4084
{
4085
  int con_port= opt_port;
4086
  char *con_options;
4087
  my_bool con_ssl= 0, con_compress= 0;
4088
  struct st_connection* con_slot;
4089
4090
  static DYNAMIC_STRING ds_connection_name;
4091
  static DYNAMIC_STRING ds_host;
4092
  static DYNAMIC_STRING ds_user;
4093
  static DYNAMIC_STRING ds_password;
4094
  static DYNAMIC_STRING ds_database;
4095
  static DYNAMIC_STRING ds_port;
4096
  static DYNAMIC_STRING ds_sock;
4097
  static DYNAMIC_STRING ds_options;
4098
  const struct command_arg connect_args[] = {
4099
    { "connection name", ARG_STRING, TRUE, &ds_connection_name, "Name of the connection" },
4100
    { "host", ARG_STRING, TRUE, &ds_host, "Host to connect to" },
4101
    { "user", ARG_STRING, FALSE, &ds_user, "User to connect as" },
4102
    { "passsword", ARG_STRING, FALSE, &ds_password, "Password used when connecting" },
4103
    { "database", ARG_STRING, FALSE, &ds_database, "Database to select after connect" },
4104
    { "port", ARG_STRING, FALSE, &ds_port, "Port to connect to" },
4105
    { "socket", ARG_STRING, FALSE, &ds_sock, "Socket to connect with" },
4106
    { "options", ARG_STRING, FALSE, &ds_options, "Options to use while connecting" }
4107
  };
4108
4109
  DBUG_ENTER("do_connect");
4110
  DBUG_PRINT("enter",("connect: %s", command->first_argument));
4111
4112
  strip_parentheses(command);
4113
  check_command_args(command, command->first_argument, connect_args,
4114
                     sizeof(connect_args)/sizeof(struct command_arg),
4115
                     ',');
4116
4117
  /* Port */
4118
  if (ds_port.length)
4119
  {
4120
    con_port= atoi(ds_port.str);
4121
    if (con_port == 0)
4122
      die("Illegal argument for port: '%s'", ds_port.str);
4123
  }
4124
4125
  /* Sock */
4126
  if (ds_sock.length)
4127
  {
4128
    /*
4129
      If the socket is specified just as a name without path
4130
      append tmpdir in front
4131
    */
4132
    if (*ds_sock.str != FN_LIBCHAR)
4133
    {
4134
      char buff[FN_REFLEN];
4135
      fn_format(buff, ds_sock.str, TMPDIR, "", 0);
4136
      dynstr_set(&ds_sock, buff);
4137
    }
4138
  }
4139
  else
4140
  {
4141
    /* No socket specified, use default */
4142
    dynstr_set(&ds_sock, unix_sock);
4143
  }
4144
  DBUG_PRINT("info", ("socket: %s", ds_sock.str));
4145
4146
4147
  /* Options */
4148
  con_options= ds_options.str;
4149
  while (*con_options)
4150
  {
4151
    char* end;
4152
    /* Step past any spaces in beginning of option*/
4153
    while (*con_options && my_isspace(charset_info, *con_options))
4154
     con_options++;
4155
    /* Find end of this option */
4156
    end= con_options;
4157
    while (*end && !my_isspace(charset_info, *end))
4158
      end++;
4159
    if (!strncmp(con_options, "SSL", 3))
4160
      con_ssl= 1;
4161
    else if (!strncmp(con_options, "COMPRESS", 8))
4162
      con_compress= 1;
4163
    else
4164
      die("Illegal option to connect: %.*s", 
4165
          (int) (end - con_options), con_options);
4166
    /* Process next option */
4167
    con_options= end;
4168
  }
4169
4170
  if (find_connection_by_name(ds_connection_name.str))
4171
    die("Connection %s already exists", ds_connection_name.str);
4172
    
4173
  if (next_con != connections_end)
4174
    con_slot= next_con;
4175
  else
4176
  {
4177
    if (!(con_slot= find_connection_by_name("-closed_connection-")))
4178
      die("Connection limit exhausted, you can have max %d connections",
4179
          (int) (sizeof(connections)/sizeof(struct st_connection)));
4180
  }
4181
4182
#ifdef EMBEDDED_LIBRARY
4183
  con_slot->query_done= 1;
4184
#endif
4185
  if (!mysql_init(&con_slot->mysql))
4186
    die("Failed on mysql_init()");
4187
  if (opt_compress || con_compress)
4188
    mysql_options(&con_slot->mysql, MYSQL_OPT_COMPRESS, NullS);
4189
  mysql_options(&con_slot->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
4190
  mysql_options(&con_slot->mysql, MYSQL_SET_CHARSET_NAME,
4191
                charset_info->csname);
4192
  int opt_protocol= MYSQL_PROTOCOL_TCP;
4193
  mysql_options(&con_slot->mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
4194
  if (opt_charsets_dir)
4195
    mysql_options(&con_slot->mysql, MYSQL_SET_CHARSET_DIR,
4196
                  opt_charsets_dir);
4197
4198
  /* Use default db name */
4199
  if (ds_database.length == 0)
4200
    dynstr_set(&ds_database, opt_db);
4201
4202
  /* Special database to allow one to connect without a database name */
4203
  if (ds_database.length && !strcmp(ds_database.str,"*NO-ONE*"))
4204
    dynstr_set(&ds_database, "");
4205
4206
  if (connect_n_handle_errors(command, &con_slot->mysql,
4207
                              ds_host.str,ds_user.str,
4208
                              ds_password.str, ds_database.str,
4209
                              con_port, ds_sock.str))
4210
  {
4211
    DBUG_PRINT("info", ("Inserting connection %s in connection pool",
4212
                        ds_connection_name.str));
4213
    if (!(con_slot->name= my_strdup(ds_connection_name.str, MYF(MY_WME))))
4214
      die("Out of memory");
4215
    cur_con= con_slot;
4216
    
4217
    if (con_slot == next_con)
4218
      next_con++; /* if we used the next_con slot, advance the pointer */
4219
  }
4220
4221
  /* Update $mysql_get_server_version to that of current connection */
4222
  var_set_mysql_get_server_version(&cur_con->mysql);
4223
4224
  dynstr_free(&ds_connection_name);
4225
  dynstr_free(&ds_host);
4226
  dynstr_free(&ds_user);
4227
  dynstr_free(&ds_password);
4228
  dynstr_free(&ds_database);
4229
  dynstr_free(&ds_port);
4230
  dynstr_free(&ds_sock);
4231
  dynstr_free(&ds_options);
4232
  DBUG_VOID_RETURN;
4233
}
4234
4235
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
4236
static int do_done(struct st_command *command)
1 by brian
clean slate
4237
{
4238
  /* Check if empty block stack */
4239
  if (cur_block == block_stack)
4240
  {
4241
    if (*command->query != '}')
4242
      die("Stray 'end' command - end of block before beginning");
4243
    die("Stray '}' - end of block before beginning");
4244
  }
4245
4246
  /* Test if inner block has been executed */
4247
  if (cur_block->ok && cur_block->cmd == cmd_while)
4248
  {
4249
    /* Pop block from stack, re-execute outer block */
4250
    cur_block--;
4251
    parser.current_line = cur_block->line;
4252
  }
4253
  else
4254
  {
4255
    /* Pop block from stack, goto next line */
4256
    cur_block--;
4257
    parser.current_line++;
4258
  }
4259
  return 0;
4260
}
4261
4262
4263
/*
4264
  Process start of a "if" or "while" statement
4265
4266
  SYNOPSIS
4267
  do_block()
4268
  cmd        Type of block
4269
  q	       called command
4270
4271
  DESCRIPTION
4272
  if ([!]<expr>)
4273
  {
4274
  <block statements>
4275
  }
4276
4277
  while ([!]<expr>)
4278
  {
4279
  <block statements>
4280
  }
4281
4282
  Evaluates the <expr> and if it evaluates to
4283
  greater than zero executes the following code block.
4284
  A '!' can be used before the <expr> to indicate it should
4285
  be executed if it evaluates to zero.
4286
4287
*/
4288
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
4289
static void do_block(enum block_cmd cmd, struct st_command* command)
1 by brian
clean slate
4290
{
4291
  char *p= command->first_argument;
4292
  const char *expr_start, *expr_end;
4293
  VAR v;
4294
  const char *cmd_name= (cmd == cmd_while ? "while" : "if");
4295
  my_bool not_expr= FALSE;
4296
  DBUG_ENTER("do_block");
4297
  DBUG_PRINT("enter", ("%s", cmd_name));
4298
4299
  /* Check stack overflow */
4300
  if (cur_block == block_stack_end)
4301
    die("Nesting too deeply");
4302
4303
  /* Set way to find outer block again, increase line counter */
4304
  cur_block->line= parser.current_line++;
4305
4306
  /* If this block is ignored */
4307
  if (!cur_block->ok)
4308
  {
4309
    /* Inner block should be ignored too */
4310
    cur_block++;
4311
    cur_block->cmd= cmd;
4312
    cur_block->ok= FALSE;
4313
    DBUG_VOID_RETURN;
4314
  }
4315
4316
  /* Parse and evaluate test expression */
4317
  expr_start= strchr(p, '(');
4318
  if (!expr_start++)
4319
    die("missing '(' in %s", cmd_name);
4320
4321
  /* Check for !<expr> */
4322
  if (*expr_start == '!')
4323
  {
4324
    not_expr= TRUE;
4325
    expr_start++; /* Step past the '!' */
4326
  }
4327
  /* Find ending ')' */
4328
  expr_end= strrchr(expr_start, ')');
4329
  if (!expr_end)
4330
    die("missing ')' in %s", cmd_name);
4331
  p= (char*)expr_end+1;
4332
4333
  while (*p && my_isspace(charset_info, *p))
4334
    p++;
4335
  if (*p && *p != '{')
4336
    die("Missing '{' after %s. Found \"%s\"", cmd_name, p);
4337
4338
  var_init(&v,0,0,0,0);
4339
  eval_expr(&v, expr_start, &expr_end);
4340
4341
  /* Define inner block */
4342
  cur_block++;
4343
  cur_block->cmd= cmd;
4344
  cur_block->ok= (v.int_val ? TRUE : FALSE);
4345
4346
  if (not_expr)
4347
    cur_block->ok = !cur_block->ok;
4348
4349
  DBUG_PRINT("info", ("OK: %d", cur_block->ok));
4350
4351
  var_free(&v);
4352
  DBUG_VOID_RETURN;
4353
}
4354
4355
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
4356
static void do_delimiter(struct st_command* command)
1 by brian
clean slate
4357
{
4358
  char* p= command->first_argument;
4359
  DBUG_ENTER("do_delimiter");
4360
  DBUG_PRINT("enter", ("first_argument: %s", command->first_argument));
4361
4362
  while (*p && my_isspace(charset_info, *p))
4363
    p++;
4364
4365
  if (!(*p))
4366
    die("Can't set empty delimiter");
4367
4368
  strmake(delimiter, p, sizeof(delimiter) - 1);
4369
  delimiter_length= strlen(delimiter);
4370
4371
  DBUG_PRINT("exit", ("delimiter: %s", delimiter));
4372
  command->last_argument= p + delimiter_length;
4373
  DBUG_VOID_RETURN;
4374
}
4375
4376
4377
my_bool match_delimiter(int c, const char *delim, uint length)
4378
{
4379
  uint i;
4380
  char tmp[MAX_DELIMITER_LENGTH];
4381
4382
  if (c != *delim)
4383
    return 0;
4384
4385
  for (i= 1; i < length &&
4386
	 (c= my_getc(cur_file->file)) == *(delim + i);
4387
       i++)
4388
    tmp[i]= c;
4389
4390
  if (i == length)
4391
    return 1;					/* Found delimiter */
4392
4393
  /* didn't find delimiter, push back things that we read */
4394
  my_ungetc(c);
4395
  while (i > 1)
4396
    my_ungetc(tmp[--i]);
4397
  return 0;
4398
}
4399
4400
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
4401
static my_bool end_of_query(int c)
1 by brian
clean slate
4402
{
4403
  return match_delimiter(c, delimiter, delimiter_length);
4404
}
4405
4406
4407
/*
4408
  Read one "line" from the file
4409
4410
  SYNOPSIS
4411
  read_line
4412
  buf     buffer for the read line
4413
  size    size of the buffer i.e max size to read
4414
4415
  DESCRIPTION
4416
  This function actually reads several lines and adds them to the
4417
  buffer buf. It continues to read until it finds what it believes
4418
  is a complete query.
4419
4420
  Normally that means it will read lines until it reaches the
4421
  "delimiter" that marks end of query. Default delimiter is ';'
4422
  The function should be smart enough not to detect delimiter's
4423
  found inside strings surrounded with '"' and '\'' escaped strings.
4424
4425
  If the first line in a query starts with '#' or '-' this line is treated
4426
  as a comment. A comment is always terminated when end of line '\n' is
4427
  reached.
4428
4429
*/
4430
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
4431
static int read_line(char *buf, int size)
1 by brian
clean slate
4432
{
4433
  char c, last_quote= 0;
4434
  char *p= buf, *buf_end= buf + size - 1;
4435
  int skip_char= 0;
4436
  enum {R_NORMAL, R_Q, R_SLASH_IN_Q,
4437
        R_COMMENT, R_LINE_START} state= R_LINE_START;
4438
  DBUG_ENTER("read_line");
4439
4440
  start_lineno= cur_file->lineno;
4441
  DBUG_PRINT("info", ("Starting to read at lineno: %d", start_lineno));
4442
  for (; p < buf_end ;)
4443
  {
4444
    skip_char= 0;
4445
    c= my_getc(cur_file->file);
4446
    if (feof(cur_file->file))
4447
    {
4448
  found_eof:
4449
      if (cur_file->file != stdin)
4450
      {
4451
	my_fclose(cur_file->file, MYF(0));
4452
        cur_file->file= 0;
4453
      }
4454
      my_free((uchar*) cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR));
4455
      cur_file->file_name= 0;
4456
      if (cur_file == file_stack)
4457
      {
4458
        /* We're back at the first file, check if
4459
           all { have matching }
4460
        */
4461
        if (cur_block != block_stack)
4462
          die("Missing end of block");
4463
4464
        *p= 0;
4465
        DBUG_PRINT("info", ("end of file at line %d", cur_file->lineno));
4466
        DBUG_RETURN(1);
4467
      }
4468
      cur_file--;
4469
      start_lineno= cur_file->lineno;
4470
      continue;
4471
    }
4472
4473
    if (c == '\n')
4474
    {
4475
      /* Line counting is independent of state */
4476
      cur_file->lineno++;
4477
4478
      /* Convert cr/lf to lf */
4479
      if (p != buf && *(p-1) == '\r')
4480
        p--;
4481
    }
4482
4483
    switch(state) {
4484
    case R_NORMAL:
4485
      if (end_of_query(c))
4486
      {
4487
	*p= 0;
4488
        DBUG_PRINT("exit", ("Found delimiter '%s' at line %d",
4489
                            delimiter, cur_file->lineno));
4490
	DBUG_RETURN(0);
4491
      }
4492
      else if ((c == '{' &&
4493
                (!my_strnncoll_simple(charset_info, (const uchar*) "while", 5,
4494
                                      (uchar*) buf, min(5, p - buf), 0) ||
4495
                 !my_strnncoll_simple(charset_info, (const uchar*) "if", 2,
4496
                                      (uchar*) buf, min(2, p - buf), 0))))
4497
      {
4498
        /* Only if and while commands can be terminated by { */
4499
        *p++= c;
4500
	*p= 0;
4501
        DBUG_PRINT("exit", ("Found '{' indicating start of block at line %d",
4502
                            cur_file->lineno));
4503
	DBUG_RETURN(0);
4504
      }
4505
      else if (c == '\'' || c == '"' || c == '`')
4506
      {
4507
        last_quote= c;
4508
	state= R_Q;
4509
      }
4510
      break;
4511
4512
    case R_COMMENT:
4513
      if (c == '\n')
4514
      {
4515
        /* Comments are terminated by newline */
4516
	*p= 0;
4517
        DBUG_PRINT("exit", ("Found newline in comment at line: %d",
4518
                            cur_file->lineno));
4519
	DBUG_RETURN(0);
4520
      }
4521
      break;
4522
4523
    case R_LINE_START:
4524
      if (c == '#' || c == '-')
4525
      {
4526
        /* A # or - in the first position of the line - this is a comment */
4527
	state = R_COMMENT;
4528
      }
4529
      else if (my_isspace(charset_info, c))
4530
      {
4531
        /* Skip all space at begining of line */
4532
	if (c == '\n')
4533
        {
4534
          /* Query hasn't started yet */
4535
	  start_lineno= cur_file->lineno;
4536
          DBUG_PRINT("info", ("Query hasn't started yet, start_lineno: %d",
4537
                              start_lineno));
4538
        }
4539
	skip_char= 1;
4540
      }
4541
      else if (end_of_query(c))
4542
      {
4543
	*p= 0;
4544
        DBUG_PRINT("exit", ("Found delimiter '%s' at line: %d",
4545
                            delimiter, cur_file->lineno));
4546
	DBUG_RETURN(0);
4547
      }
4548
      else if (c == '}')
4549
      {
4550
        /* A "}" need to be by itself in the begining of a line to terminate */
4551
        *p++= c;
4552
	*p= 0;
4553
        DBUG_PRINT("exit", ("Found '}' in begining of a line at line: %d",
4554
                            cur_file->lineno));
4555
	DBUG_RETURN(0);
4556
      }
4557
      else if (c == '\'' || c == '"' || c == '`')
4558
      {
4559
        last_quote= c;
4560
	state= R_Q;
4561
      }
4562
      else
4563
	state= R_NORMAL;
4564
      break;
4565
4566
    case R_Q:
4567
      if (c == last_quote)
4568
	state= R_NORMAL;
4569
      else if (c == '\\')
4570
	state= R_SLASH_IN_Q;
4571
      break;
4572
4573
    case R_SLASH_IN_Q:
4574
      state= R_Q;
4575
      break;
4576
4577
    }
4578
4579
    if (!skip_char)
4580
    {
4581
      /* Could be a multibyte character */
4582
      /* This code is based on the code in "sql_load.cc" */
4583
#ifdef USE_MB
4584
      int charlen = my_mbcharlen(charset_info, c);
4585
      /* We give up if multibyte character is started but not */
4586
      /* completed before we pass buf_end */
4587
      if ((charlen > 1) && (p + charlen) <= buf_end)
4588
      {
4589
	int i;
4590
	char* mb_start = p;
4591
4592
	*p++ = c;
4593
4594
	for (i= 1; i < charlen; i++)
4595
	{
4596
	  if (feof(cur_file->file))
4597
	    goto found_eof;
4598
	  c= my_getc(cur_file->file);
4599
	  *p++ = c;
4600
	}
4601
	if (! my_ismbchar(charset_info, mb_start, p))
4602
	{
4603
	  /* It was not a multiline char, push back the characters */
4604
	  /* We leave first 'c', i.e. pretend it was a normal char */
4605
	  while (p > mb_start)
4606
	    my_ungetc(*--p);
4607
	}
4608
      }
4609
      else
4610
#endif
4611
	*p++= c;
4612
    }
4613
  }
4614
  die("The input buffer is too small for this query.x\n" \
4615
      "check your query or increase MAX_QUERY and recompile");
4616
  DBUG_RETURN(0);
4617
}
4618
4619
4620
/*
4621
  Convert the read query to result format version 1
4622
4623
  That is: After newline, all spaces need to be skipped
4624
  unless the previous char was a quote
4625
4626
  This is due to an old bug that has now been fixed, but the
4627
  version 1 output format is preserved by using this function
4628
4629
*/
4630
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
4631
static void convert_to_format_v1(char* query)
1 by brian
clean slate
4632
{
4633
  int last_c_was_quote= 0;
4634
  char *p= query, *to= query;
4635
  char *end= strend(query);
4636
  char last_c;
4637
4638
  while (p <= end)
4639
  {
4640
    if (*p == '\n' && !last_c_was_quote)
4641
    {
4642
      *to++ = *p++; /* Save the newline */
4643
4644
      /* Skip any spaces on next line */
4645
      while (*p && my_isspace(charset_info, *p))
4646
        p++;
4647
4648
      last_c_was_quote= 0;
4649
    }
4650
    else if (*p == '\'' || *p == '"' || *p == '`')
4651
    {
4652
      last_c= *p;
4653
      *to++ = *p++;
4654
4655
      /* Copy anything until the next quote of same type */
4656
      while (*p && *p != last_c)
4657
        *to++ = *p++;
4658
4659
      *to++ = *p++;
4660
4661
      last_c_was_quote= 1;
4662
    }
4663
    else
4664
    {
4665
      *to++ = *p++;
4666
      last_c_was_quote= 0;
4667
    }
4668
  }
4669
}
4670
4671
4672
/*
4673
  Check a command that is about to be sent (or should have been
4674
  sent if parsing was enabled) to mysql server for
4675
  suspicious things and generate warnings.
4676
*/
4677
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
4678
static void scan_command_for_warnings(struct st_command *command)
1 by brian
clean slate
4679
{
4680
  const char *ptr= command->query;
4681
  DBUG_ENTER("scan_command_for_warnings");
4682
  DBUG_PRINT("enter", ("query: %s", command->query));
4683
4684
  while(*ptr)
4685
  {
4686
    /*
4687
      Look for query's that lines that start with a -- comment
4688
      and has a mysqltest command
4689
    */
4690
    if (ptr[0] == '\n' &&
4691
        ptr[1] && ptr[1] == '-' &&
4692
        ptr[2] && ptr[2] == '-' &&
4693
        ptr[3])
4694
    {
4695
      uint type;
4696
      char save;
4697
      char *end, *start= (char*)ptr+3;
4698
      /* Skip leading spaces */
4699
      while (*start && my_isspace(charset_info, *start))
4700
        start++;
4701
      end= start;
4702
      /* Find end of command(next space) */
4703
      while (*end && !my_isspace(charset_info, *end))
4704
        end++;
4705
      save= *end;
4706
      *end= 0;
4707
      DBUG_PRINT("info", ("Checking '%s'", start));
4708
      type= find_type(start, &command_typelib, 1+2);
4709
      if (type)
4710
        warning_msg("Embedded mysqltest command '--%s' detected in "
4711
                    "query '%s' was this intentional? ",
4712
                    start, command->query);
4713
      *end= save;
4714
    }
4715
4716
    ptr++;
4717
  }
4718
  DBUG_VOID_RETURN;
4719
}
4720
4721
/*
4722
  Check for unexpected "junk" after the end of query
4723
  This is normally caused by missing delimiters or when
4724
  switching between different delimiters
4725
*/
4726
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
4727
static void check_eol_junk_line(const char *line)
1 by brian
clean slate
4728
{
4729
  const char *p= line;
4730
  DBUG_ENTER("check_eol_junk_line");
4731
  DBUG_PRINT("enter", ("line: %s", line));
4732
4733
  /* Check for extra delimiter */
4734
  if (*p && !strncmp(p, delimiter, delimiter_length))
4735
    die("Extra delimiter \"%s\" found", delimiter);
4736
4737
  /* Allow trailing # comment */
4738
  if (*p && *p != '#')
4739
  {
4740
    if (*p == '\n')
4741
      die("Missing delimiter");
4742
    die("End of line junk detected: \"%s\"", p);
4743
  }
4744
  DBUG_VOID_RETURN;
4745
}
4746
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
4747
static void check_eol_junk(const char *eol)
1 by brian
clean slate
4748
{
4749
  const char *p= eol;
4750
  DBUG_ENTER("check_eol_junk");
4751
  DBUG_PRINT("enter", ("eol: %s", eol));
4752
4753
  /* Skip past all spacing chars and comments */
4754
  while (*p && (my_isspace(charset_info, *p) || *p == '#' || *p == '\n'))
4755
  {
4756
    /* Skip past comments started with # and ended with newline */
4757
    if (*p && *p == '#')
4758
    {
4759
      p++;
4760
      while (*p && *p != '\n')
4761
        p++;
4762
    }
4763
4764
    /* Check this line */
4765
    if (*p && *p == '\n')
4766
      check_eol_junk_line(p);
4767
4768
    if (*p)
4769
      p++;
4770
  }
4771
4772
  check_eol_junk_line(p);
4773
4774
  DBUG_VOID_RETURN;
4775
}
4776
4777
4778
4779
/*
4780
  Create a command from a set of lines
4781
4782
  SYNOPSIS
4783
    read_command()
4784
    command_ptr pointer where to return the new query
4785
4786
  DESCRIPTION
4787
    Converts lines returned by read_line into a command, this involves
4788
    parsing the first word in the read line to find the command type.
4789
4790
  A -- comment may contain a valid query as the first word after the
4791
  comment start. Thus it's always checked to see if that is the case.
4792
  The advantage with this approach is to be able to execute commands
4793
  terminated by new line '\n' regardless how many "delimiter" it contain.
4794
*/
4795
4796
#define MAX_QUERY (256*1024*2) /* 256K -- a test in sp-big is >128K */
4797
static char read_command_buf[MAX_QUERY];
4798
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
4799
static int read_command(struct st_command** command_ptr)
1 by brian
clean slate
4800
{
4801
  char *p= read_command_buf;
4802
  struct st_command* command;
4803
  DBUG_ENTER("read_command");
4804
4805
  if (parser.current_line < parser.read_lines)
4806
  {
4807
    get_dynamic(&q_lines, (uchar*) command_ptr, parser.current_line) ;
4808
    DBUG_RETURN(0);
4809
  }
4810
  if (!(*command_ptr= command=
4811
        (struct st_command*) my_malloc(sizeof(*command),
4812
                                       MYF(MY_WME|MY_ZEROFILL))) ||
4813
      insert_dynamic(&q_lines, (uchar*) &command))
4814
    die(NullS);
4815
  command->type= Q_UNKNOWN;
4816
4817
  read_command_buf[0]= 0;
4818
  if (read_line(read_command_buf, sizeof(read_command_buf)))
4819
  {
4820
    check_eol_junk(read_command_buf);
4821
    DBUG_RETURN(1);
4822
  }
4823
4824
  convert_to_format_v1(read_command_buf);
4825
4826
  DBUG_PRINT("info", ("query: %s", read_command_buf));
4827
  if (*p == '#')
4828
  {
4829
    command->type= Q_COMMENT;
4830
  }
4831
  else if (p[0] == '-' && p[1] == '-')
4832
  {
4833
    command->type= Q_COMMENT_WITH_COMMAND;
4834
    p+= 2; /* Skip past -- */
4835
  }
4836
4837
  /* Skip leading spaces */
4838
  while (*p && my_isspace(charset_info, *p))
4839
    p++;
4840
4841
  if (!(command->query_buf= command->query= my_strdup(p, MYF(MY_WME))))
4842
    die("Out of memory");
4843
4844
  /* Calculate first word length(the command), terminated by space or ( */
4845
  p= command->query;
4846
  while (*p && !my_isspace(charset_info, *p) && *p != '(')
4847
    p++;
4848
  command->first_word_len= (uint) (p - command->query);
4849
  DBUG_PRINT("info", ("first_word: %.*s",
4850
                      command->first_word_len, command->query));
4851
4852
  /* Skip spaces between command and first argument */
4853
  while (*p && my_isspace(charset_info, *p))
4854
    p++;
4855
  command->first_argument= p;
4856
4857
  command->end= strend(command->query);
4858
  command->query_len= (command->end - command->query);
4859
  parser.read_lines++;
4860
  DBUG_RETURN(0);
4861
}
4862
4863
4864
static struct my_option my_long_options[] =
4865
{
4866
  {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
4867
   0, 0, 0, 0, 0, 0},
4868
  {"basedir", 'b', "Basedir for tests.", (uchar**) &opt_basedir,
4869
   (uchar**) &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
4870
  {"character-sets-dir", OPT_CHARSETS_DIR,
4871
   "Directory where character sets are.", (uchar**) &opt_charsets_dir,
4872
   (uchar**) &opt_charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
4873
  {"compress", 'C', "Use the compressed server/client protocol.",
4874
   (uchar**) &opt_compress, (uchar**) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
4875
   0, 0, 0},
4876
  {"database", 'D', "Database to use.", (uchar**) &opt_db, (uchar**) &opt_db, 0,
4877
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
4878
#ifdef DBUG_OFF
4879
  {"debug", '#', "This is a non-debug version. Catch this and exit",
4880
   0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
4881
#else
4882
  {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
4883
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
4884
#endif
4885
  {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.",
4886
   (uchar**) &debug_check_flag, (uchar**) &debug_check_flag, 0,
4887
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
4888
  {"debug-info", OPT_DEBUG_INFO, "Print some debug info at exit.",
4889
   (uchar**) &debug_info_flag, (uchar**) &debug_info_flag,
4890
   0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
4891
  {"host", 'h', "Connect to host.", (uchar**) &opt_host, (uchar**) &opt_host, 0,
4892
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
4893
  {"include", 'i', "Include SQL before each test case.", (uchar**) &opt_include,
4894
   (uchar**) &opt_include, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
4895
  {"logdir", OPT_LOG_DIR, "Directory for log files", (uchar**) &opt_logdir,
4896
   (uchar**) &opt_logdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
4897
  {"mark-progress", OPT_MARK_PROGRESS,
4898
   "Write linenumber and elapsed time to <testname>.progress ",
4899
   (uchar**) &opt_mark_progress, (uchar**) &opt_mark_progress, 0,
4900
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
4901
  {"max-connect-retries", OPT_MAX_CONNECT_RETRIES,
4902
   "Max number of connection attempts when connecting to server",
4903
   (uchar**) &opt_max_connect_retries, (uchar**) &opt_max_connect_retries, 0,
4904
   GET_INT, REQUIRED_ARG, 500, 1, 10000, 0, 0, 0},
4905
  {"password", 'p', "Password to use when connecting to server.",
4906
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
4907
  {"port", 'P', "Port number to use for connection or 0 for default to, in "
4908
   "order of preference, my.cnf, $MYSQL_TCP_PORT, "
4909
#if MYSQL_PORT_DEFAULT == 0
4910
   "/etc/services, "
4911
#endif
4912
   "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
4913
   (uchar**) &opt_port,
4914
   (uchar**) &opt_port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
4915
  {"quiet", 's', "Suppress all normal output.", (uchar**) &silent,
4916
   (uchar**) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
4917
  {"record", 'r', "Record output of test_file into result file.",
4918
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
4919
  {"result-file", 'R', "Read/Store result from/in this file.",
4920
   (uchar**) &result_file_name, (uchar**) &result_file_name, 0,
4921
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
4922
  {"server-arg", 'A', "Send option value to embedded server as a parameter.",
4923
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
4924
  {"server-file", 'F', "Read embedded server arguments from file.",
4925
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
4926
  {"silent", 's', "Suppress all normal output. Synonym for --quiet.",
4927
   (uchar**) &silent, (uchar**) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
4928
  {"sleep", 'T', "Sleep always this many seconds on sleep commands.",
4929
   (uchar**) &opt_sleep, (uchar**) &opt_sleep, 0, GET_INT, REQUIRED_ARG, -1, -1, 0,
4930
   0, 0, 0},
4931
  {"tail-lines", OPT_TAIL_LINES,
4932
   "Number of lines of the resul to include in a failure report",
4933
   (uchar**) &opt_tail_lines, (uchar**) &opt_tail_lines, 0,
4934
   GET_INT, REQUIRED_ARG, 0, 0, 10000, 0, 0, 0},
4935
  {"test-file", 'x', "Read test from/in this file (default stdin).",
4936
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
4937
  {"timer-file", 'm', "File where the timing in micro seconds is stored.",
4938
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
4939
  {"tmpdir", 't', "Temporary directory where sockets are put.",
4940
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
4941
  {"user", 'u', "User for login.", (uchar**) &opt_user, (uchar**) &opt_user, 0,
4942
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
4943
  {"verbose", 'v', "Write more.", (uchar**) &verbose, (uchar**) &verbose, 0,
4944
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
4945
  {"version", 'V', "Output version information and exit.",
4946
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
4947
  { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
4948
};
4949
4950
4951
#include <help_start.h>
4952
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
4953
static void print_version(void)
1 by brian
clean slate
4954
{
4955
  printf("%s  Ver %s Distrib %s, for %s (%s)\n",my_progname,MTEST_VERSION,
4956
	 MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE);
4957
}
4958
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
4959
static void usage(void)
1 by brian
clean slate
4960
{
4961
  print_version();
4962
  printf("MySQL AB, by Sasha, Matt, Monty & Jani\n");
4963
  printf("This software comes with ABSOLUTELY NO WARRANTY\n\n");
4964
  printf("Runs a test against the mysql server and compares output with a results file.\n\n");
4965
  printf("Usage: %s [OPTIONS] [database] < test_file\n", my_progname);
4966
  my_print_help(my_long_options);
4967
  printf("  --no-defaults       Don't read default options from any options file.\n");
4968
  my_print_variables(my_long_options);
4969
}
4970
4971
#include <help_end.h>
4972
4973
4974
/*
4975
  Read arguments for embedded server and put them into
4976
  embedded_server_args[]
4977
*/
4978
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
4979
static void read_embedded_server_arguments(const char *name)
1 by brian
clean slate
4980
{
4981
  char argument[1024],buff[FN_REFLEN], *str=0;
4982
  FILE *file;
4983
4984
  if (!test_if_hard_path(name))
4985
  {
4986
    strxmov(buff, opt_basedir, name, NullS);
4987
    name=buff;
4988
  }
4989
  fn_format(buff, name, "", "", MY_UNPACK_FILENAME);
4990
4991
  if (!embedded_server_arg_count)
4992
  {
4993
    embedded_server_arg_count=1;
4994
    embedded_server_args[0]= (char*) "";		/* Progname */
4995
  }
4996
  if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
4997
    die("Failed to open file '%s'", buff);
4998
4999
  while (embedded_server_arg_count < MAX_EMBEDDED_SERVER_ARGS &&
5000
	 (str=fgets(argument,sizeof(argument), file)))
5001
  {
5002
    *(strend(str)-1)=0;				/* Remove end newline */
5003
    if (!(embedded_server_args[embedded_server_arg_count]=
5004
	  (char*) my_strdup(str,MYF(MY_WME))))
5005
    {
5006
      my_fclose(file,MYF(0));
5007
      die("Out of memory");
5008
5009
    }
5010
    embedded_server_arg_count++;
5011
  }
5012
  my_fclose(file,MYF(0));
5013
  if (str)
5014
    die("Too many arguments in option file: %s",name);
5015
5016
  return;
5017
}
5018
5019
5020
static my_bool
5021
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
5022
	       char *argument)
5023
{
5024
  switch(optid) {
5025
  case '#':
5026
#ifndef DBUG_OFF
5027
    DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysqltest.trace");
5028
    debug_check_flag= 1;
5029
#endif
5030
    break;
5031
  case 'r':
5032
    record = 1;
5033
    break;
5034
  case 'x':
5035
  {
5036
    char buff[FN_REFLEN];
5037
    if (!test_if_hard_path(argument))
5038
    {
5039
      strxmov(buff, opt_basedir, argument, NullS);
5040
      argument= buff;
5041
    }
5042
    fn_format(buff, argument, "", "", MY_UNPACK_FILENAME);
5043
    DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0);
5044
    if (!(cur_file->file=
5045
          my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0))))
5046
      die("Could not open '%s' for reading: errno = %d", buff, errno);
5047
    cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
5048
    cur_file->lineno= 1;
5049
    break;
5050
  }
5051
  case 'm':
5052
  {
5053
    static char buff[FN_REFLEN];
5054
    if (!test_if_hard_path(argument))
5055
    {
5056
      strxmov(buff, opt_basedir, argument, NullS);
5057
      argument= buff;
5058
    }
5059
    fn_format(buff, argument, "", "", MY_UNPACK_FILENAME);
5060
    timer_file= buff;
5061
    unlink(timer_file);	     /* Ignore error, may not exist */
5062
    break;
5063
  }
5064
  case 'p':
5065
    if (argument)
5066
    {
5067
      my_free(opt_pass, MYF(MY_ALLOW_ZERO_PTR));
5068
      opt_pass= my_strdup(argument, MYF(MY_FAE));
5069
      while (*argument) *argument++= 'x';		/* Destroy argument */
5070
      tty_password= 0;
5071
    }
5072
    else
5073
      tty_password= 1;
5074
    break;
5075
  case 't':
5076
    strnmov(TMPDIR, argument, sizeof(TMPDIR));
5077
    break;
5078
  case 'A':
5079
    if (!embedded_server_arg_count)
5080
    {
5081
      embedded_server_arg_count=1;
5082
      embedded_server_args[0]= (char*) "";
5083
    }
5084
    if (embedded_server_arg_count == MAX_EMBEDDED_SERVER_ARGS-1 ||
5085
        !(embedded_server_args[embedded_server_arg_count++]=
5086
          my_strdup(argument, MYF(MY_FAE))))
5087
    {
5088
      die("Can't use server argument");
5089
    }
5090
    break;
5091
  case 'F':
5092
    read_embedded_server_arguments(argument);
5093
    break;
5094
  case 'V':
5095
    print_version();
5096
    exit(0);
5097
  case '?':
5098
    usage();
5099
    exit(0);
5100
  }
5101
  return 0;
5102
}
5103
5104
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
5105
static int parse_args(int argc, char **argv)
1 by brian
clean slate
5106
{
5107
  load_defaults("my",load_default_groups,&argc,&argv);
5108
  default_argv= argv;
5109
5110
  if ((handle_options(&argc, &argv, my_long_options, get_one_option)))
5111
    exit(1);
5112
5113
  if (argc > 1)
5114
  {
5115
    usage();
5116
    exit(1);
5117
  }
5118
  if (argc == 1)
5119
    opt_db= *argv;
5120
  if (tty_password)
5121
    opt_pass= get_tty_password(NullS);          /* purify tested */
5122
  if (debug_info_flag)
5123
    my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
5124
  if (debug_check_flag)
5125
    my_end_arg= MY_CHECK_ERROR;
5126
5127
  return 0;
5128
}
5129
5130
/*
5131
  Write the content of str into file
5132
5133
  SYNOPSIS
5134
  str_to_file2
5135
  fname - name of file to truncate/create and write to
5136
  str - content to write to file
5137
  size - size of content witten to file
5138
  append - append to file instead of overwriting old file
5139
*/
5140
5141
void str_to_file2(const char *fname, char *str, int size, my_bool append)
5142
{
5143
  int fd;
5144
  char buff[FN_REFLEN];
5145
  int flags= O_WRONLY | O_CREAT;
5146
  if (!test_if_hard_path(fname))
5147
  {
5148
    strxmov(buff, opt_basedir, fname, NullS);
5149
    fname= buff;
5150
  }
5151
  fn_format(buff, fname, "", "", MY_UNPACK_FILENAME);
5152
5153
  if (!append)
5154
    flags|= O_TRUNC;
5155
  if ((fd= my_open(buff, flags,
5156
                   MYF(MY_WME | MY_FFNF))) < 0)
5157
    die("Could not open '%s' for writing: errno = %d", buff, errno);
5158
  if (append && my_seek(fd, 0, SEEK_END, MYF(0)) == MY_FILEPOS_ERROR)
5159
    die("Could not find end of file '%s': errno = %d", buff, errno);
5160
  if (my_write(fd, (uchar*)str, size, MYF(MY_WME|MY_FNABP)))
5161
    die("write failed");
5162
  my_close(fd, MYF(0));
5163
}
5164
5165
/*
5166
  Write the content of str into file
5167
5168
  SYNOPSIS
5169
  str_to_file
5170
  fname - name of file to truncate/create and write to
5171
  str - content to write to file
5172
  size - size of content witten to file
5173
*/
5174
5175
void str_to_file(const char *fname, char *str, int size)
5176
{
5177
  str_to_file2(fname, str, size, FALSE);
5178
}
5179
5180
5181
void dump_result_to_log_file(char *buf, int size)
5182
{
5183
  char log_file[FN_REFLEN];
5184
  str_to_file(fn_format(log_file, result_file_name, opt_logdir, ".log",
5185
                        *opt_logdir ? MY_REPLACE_DIR | MY_REPLACE_EXT :
5186
                        MY_REPLACE_EXT),
5187
              buf, size);
5188
  fprintf(stderr, "\nMore results from queries before failure can be found in %s\n",
5189
          log_file);
5190
}
5191
5192
void dump_progress(void)
5193
{
5194
  char progress_file[FN_REFLEN];
5195
  str_to_file(fn_format(progress_file, result_file_name,
5196
                        opt_logdir, ".progress",
5197
                        *opt_logdir ? MY_REPLACE_DIR | MY_REPLACE_EXT :
5198
                        MY_REPLACE_EXT),
5199
              ds_progress.str, ds_progress.length);
5200
}
5201
5202
void dump_warning_messages(void)
5203
{
5204
  char warn_file[FN_REFLEN];
5205
5206
  str_to_file(fn_format(warn_file, result_file_name, opt_logdir, ".warnings",
5207
                        *opt_logdir ? MY_REPLACE_DIR | MY_REPLACE_EXT :
5208
                        MY_REPLACE_EXT),
5209
              ds_warning_messages.str, ds_warning_messages.length);
5210
}
5211
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
5212
static void check_regerr(my_regex_t* r, int err)
1 by brian
clean slate
5213
{
5214
  char err_buf[1024];
5215
5216
  if (err)
5217
  {
5218
    my_regerror(err,r,err_buf,sizeof(err_buf));
5219
    die("Regex error: %s\n", err_buf);
5220
  }
5221
}
5222
5223
/*
5224
  Append the result for one field to the dynamic string ds
5225
*/
5226
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
5227
static void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD* field,
5228
                         const char* val, ulonglong len, my_bool is_null)
1 by brian
clean slate
5229
{
5230
  if (col_idx < max_replace_column && replace_column[col_idx])
5231
  {
5232
    val= replace_column[col_idx];
5233
    len= strlen(val);
5234
  }
5235
  else if (is_null)
5236
  {
5237
    val= "NULL";
5238
    len= 4;
5239
  }
5240
5241
  if (!display_result_vertically)
5242
  {
5243
    if (col_idx)
5244
      dynstr_append_mem(ds, "\t", 1);
5245
    replace_dynstr_append_mem(ds, val, (int)len);
5246
  }
5247
  else
5248
  {
5249
    dynstr_append(ds, field->name);
5250
    dynstr_append_mem(ds, "\t", 1);
5251
    replace_dynstr_append_mem(ds, val, (int)len);
5252
    dynstr_append_mem(ds, "\n", 1);
5253
  }
5254
}
5255
5256
5257
/*
5258
  Append all results to the dynamic string separated with '\t'
5259
  Values may be converted with 'replace_column'
5260
*/
5261
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
5262
static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
1 by brian
clean slate
5263
{
5264
  MYSQL_ROW row;
5265
  uint num_fields= mysql_num_fields(res);
5266
  MYSQL_FIELD *fields= mysql_fetch_fields(res);
5267
  ulong *lengths;
5268
5269
  while ((row = mysql_fetch_row(res)))
5270
  {
5271
    uint i;
5272
    lengths = mysql_fetch_lengths(res);
5273
    for (i = 0; i < num_fields; i++)
5274
      append_field(ds, i, &fields[i],
5275
                   (const char*)row[i], lengths[i], !row[i]);
5276
    if (!display_result_vertically)
5277
      dynstr_append_mem(ds, "\n", 1);
5278
  }
5279
}
5280
5281
5282
/*
5283
  Append metadata for fields to output
5284
*/
5285
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
5286
static void append_metadata(DYNAMIC_STRING *ds,
5287
                            MYSQL_FIELD *field,
5288
                            uint num_fields)
1 by brian
clean slate
5289
{
5290
  MYSQL_FIELD *field_end;
5291
  dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t"
5292
                "Column_alias\tType\tLength\tMax length\tIs_null\t"
5293
                "Flags\tDecimals\tCharsetnr\n");
5294
5295
  for (field_end= field+num_fields ;
5296
       field < field_end ;
5297
       field++)
5298
  {
5299
    dynstr_append_mem(ds, field->catalog,
5300
                      field->catalog_length);
5301
    dynstr_append_mem(ds, "\t", 1);
5302
    dynstr_append_mem(ds, field->db, field->db_length);
5303
    dynstr_append_mem(ds, "\t", 1);
5304
    dynstr_append_mem(ds, field->org_table,
5305
                      field->org_table_length);
5306
    dynstr_append_mem(ds, "\t", 1);
5307
    dynstr_append_mem(ds, field->table,
5308
                      field->table_length);
5309
    dynstr_append_mem(ds, "\t", 1);
5310
    dynstr_append_mem(ds, field->org_name,
5311
                      field->org_name_length);
5312
    dynstr_append_mem(ds, "\t", 1);
5313
    dynstr_append_mem(ds, field->name, field->name_length);
5314
    dynstr_append_mem(ds, "\t", 1);
5315
    replace_dynstr_append_uint(ds, field->type);
5316
    dynstr_append_mem(ds, "\t", 1);
5317
    replace_dynstr_append_uint(ds, field->length);
5318
    dynstr_append_mem(ds, "\t", 1);
5319
    replace_dynstr_append_uint(ds, field->max_length);
5320
    dynstr_append_mem(ds, "\t", 1);
5321
    dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ?
5322
                                   "N" : "Y"), 1);
5323
    dynstr_append_mem(ds, "\t", 1);
5324
    replace_dynstr_append_uint(ds, field->flags);
5325
    dynstr_append_mem(ds, "\t", 1);
5326
    replace_dynstr_append_uint(ds, field->decimals);
5327
    dynstr_append_mem(ds, "\t", 1);
5328
    replace_dynstr_append_uint(ds, field->charsetnr);
5329
    dynstr_append_mem(ds, "\n", 1);
5330
  }
5331
}
5332
5333
5334
/*
5335
  Append affected row count and other info to output
5336
*/
5337
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
5338
static void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows,
5339
                        const char *info)
1 by brian
clean slate
5340
{
5341
  char buf[40], buff2[21];
5342
  sprintf(buf,"affected rows: %s\n", llstr(affected_rows, buff2));
5343
  dynstr_append(ds, buf);
5344
  if (info)
5345
  {
5346
    dynstr_append(ds, "info: ");
5347
    dynstr_append(ds, info);
5348
    dynstr_append_mem(ds, "\n", 1);
5349
  }
5350
}
5351
5352
5353
/*
5354
  Display the table headings with the names tab separated
5355
*/
5356
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
5357
static void append_table_headings(DYNAMIC_STRING *ds,
5358
                                  MYSQL_FIELD *field,
5359
                                  uint num_fields)
1 by brian
clean slate
5360
{
5361
  uint col_idx;
5362
  for (col_idx= 0; col_idx < num_fields; col_idx++)
5363
  {
5364
    if (col_idx)
5365
      dynstr_append_mem(ds, "\t", 1);
5366
    replace_dynstr_append(ds, field[col_idx].name);
5367
  }
5368
  dynstr_append_mem(ds, "\n", 1);
5369
}
5370
5371
/*
5372
  Fetch warnings from server and append to ds
5373
5374
  RETURN VALUE
5375
  Number of warnings appended to ds
5376
*/
5377
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
5378
static int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql)
1 by brian
clean slate
5379
{
5380
  uint count;
5381
  MYSQL_RES *warn_res;
5382
  DBUG_ENTER("append_warnings");
5383
5384
  if (!(count= mysql_warning_count(mysql)))
5385
    DBUG_RETURN(0);
5386
5387
  /*
5388
    If one day we will support execution of multi-statements
5389
    through PS API we should not issue SHOW WARNINGS until
5390
    we have not read all results...
5391
  */
5392
  DBUG_ASSERT(!mysql_more_results(mysql));
5393
5394
  if (mysql_real_query(mysql, "SHOW WARNINGS", 13))
5395
    die("Error running query \"SHOW WARNINGS\": %s", mysql_error(mysql));
5396
5397
  if (!(warn_res= mysql_store_result(mysql)))
5398
    die("Warning count is %u but didn't get any warnings",
5399
	count);
5400
5401
  append_result(ds, warn_res);
5402
  mysql_free_result(warn_res);
5403
5404
  DBUG_PRINT("warnings", ("%s", ds->str));
5405
5406
  DBUG_RETURN(count);
5407
}
5408
5409
5410
/*
5411
  Run query using MySQL C API
5412
5413
  SYNOPSIS
5414
    run_query_normal()
5415
    mysql	mysql handle
5416
    command	current command pointer
5417
    flags	flags indicating if we should SEND and/or REAP
5418
    query	query string to execute
5419
    query_len	length query string to execute
5420
    ds		output buffer where to store result form query
5421
*/
5422
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
5423
static void run_query_normal(struct st_connection *cn,
5424
                             struct st_command *command,
5425
                             int flags, char *query, int query_len,
5426
                             DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings)
1 by brian
clean slate
5427
{
5428
  MYSQL_RES *res= 0;
5429
  MYSQL *mysql= &cn->mysql;
5430
  int err= 0, counter= 0;
5431
  DBUG_ENTER("run_query_normal");
5432
  DBUG_PRINT("enter",("flags: %d", flags));
5433
  DBUG_PRINT("enter", ("query: '%-.60s'", query));
5434
5435
  if (flags & QUERY_SEND_FLAG)
5436
  {
5437
    /*
5438
      Send the query
5439
    */
5440
    if (do_send_query(cn, query, query_len, flags))
5441
    {
5442
      handle_error(command, mysql_errno(mysql), mysql_error(mysql),
5443
		   mysql_sqlstate(mysql), ds);
5444
      goto end;
5445
    }
5446
  }
5447
#ifdef EMBEDDED_LIBRARY
5448
  /*
5449
    Here we handle 'reap' command, so we need to check if the
5450
    query's thread was finished and probably wait
5451
  */
5452
  else if (flags & QUERY_REAP_FLAG)
5453
    wait_query_thread_end(cn);
5454
#endif /*EMBEDDED_LIBRARY*/
5455
  if (!(flags & QUERY_REAP_FLAG))
5456
    DBUG_VOID_RETURN;
5457
5458
  do
5459
  {
5460
    /*
5461
      When  on first result set, call mysql_read_query_result to retrieve
5462
      answer to the query sent earlier
5463
    */
5464
    if ((counter==0) && mysql_read_query_result(mysql))
5465
    {
5466
      handle_error(command, mysql_errno(mysql), mysql_error(mysql),
5467
		   mysql_sqlstate(mysql), ds);
5468
      goto end;
5469
5470
    }
5471
5472
    /*
5473
      Store the result of the query if it will return any fields
5474
    */
5475
    if (mysql_field_count(mysql) && ((res= mysql_store_result(mysql)) == 0))
5476
    {
5477
      handle_error(command, mysql_errno(mysql), mysql_error(mysql),
5478
		   mysql_sqlstate(mysql), ds);
5479
      goto end;
5480
    }
5481
5482
    if (!disable_result_log)
5483
    {
5484
      ulonglong affected_rows= 0;    /* Ok to be undef if 'disable_info' is set */
5485
5486
      if (res)
5487
      {
5488
	MYSQL_FIELD *fields= mysql_fetch_fields(res);
5489
	uint num_fields= mysql_num_fields(res);
5490
5491
	if (display_metadata)
5492
          append_metadata(ds, fields, num_fields);
5493
5494
	if (!display_result_vertically)
5495
	  append_table_headings(ds, fields, num_fields);
5496
5497
	append_result(ds, res);
5498
      }
5499
5500
      /*
5501
        Need to call mysql_affected_rows() before the "new"
5502
        query to find the warnings
5503
      */
5504
      if (!disable_info)
5505
        affected_rows= mysql_affected_rows(mysql);
5506
5507
      /*
5508
        Add all warnings to the result. We can't do this if we are in
5509
        the middle of processing results from multi-statement, because
5510
        this will break protocol.
5511
      */
5512
      if (!disable_warnings && !mysql_more_results(mysql))
5513
      {
5514
	if (append_warnings(ds_warnings, mysql) || ds_warnings->length)
5515
	{
5516
	  dynstr_append_mem(ds, "Warnings:\n", 10);
5517
	  dynstr_append_mem(ds, ds_warnings->str, ds_warnings->length);
5518
	}
5519
      }
5520
5521
      if (!disable_info)
5522
	append_info(ds, affected_rows, mysql_info(mysql));
5523
    }
5524
5525
    if (res)
5526
    {
5527
      mysql_free_result(res);
5528
      res= 0;
5529
    }
5530
    counter++;
5531
  } while (!(err= mysql_next_result(mysql)));
5532
  if (err > 0)
5533
  {
5534
    /* We got an error from mysql_next_result, maybe expected */
5535
    handle_error(command, mysql_errno(mysql), mysql_error(mysql),
5536
		 mysql_sqlstate(mysql), ds);
5537
    goto end;
5538
  }
5539
  DBUG_ASSERT(err == -1); /* Successful and there are no more results */
5540
5541
  /* If we come here the query is both executed and read successfully */
5542
  handle_no_error(command);
5543
5544
end:
5545
5546
  /*
5547
    We save the return code (mysql_errno(mysql)) from the last call sent
5548
    to the server into the mysqltest builtin variable $mysql_errno. This
5549
    variable then can be used from the test case itself.
5550
  */
5551
  var_set_errno(mysql_errno(mysql));
5552
  DBUG_VOID_RETURN;
5553
}
5554
5555
5556
/*
5557
  Handle errors which occurred during execution
5558
5559
  SYNOPSIS
5560
  handle_error()
5561
  q     - query context
5562
  err_errno - error number
5563
  err_error - error message
5564
  err_sqlstate - sql state
5565
  ds    - dynamic string which is used for output buffer
5566
5567
  NOTE
5568
    If there is an unexpected error this function will abort mysqltest
5569
    immediately.
5570
*/
5571
5572
void handle_error(struct st_command *command,
5573
                  unsigned int err_errno, const char *err_error,
5574
                  const char *err_sqlstate, DYNAMIC_STRING *ds)
5575
{
5576
  uint i;
5577
5578
  DBUG_ENTER("handle_error");
5579
5580
  if (command->require_file[0])
5581
  {
5582
    /*
5583
      The query after a "--require" failed. This is fine as long the server
5584
      returned a valid reponse. Don't allow 2013 or 2006 to trigger an
5585
      abort_not_supported_test
5586
    */
5587
    if (err_errno == CR_SERVER_LOST ||
5588
        err_errno == CR_SERVER_GONE_ERROR)
5589
      die("require query '%s' failed: %d: %s", command->query,
5590
          err_errno, err_error);
5591
5592
    /* Abort the run of this test, pass the failed query as reason */
5593
    abort_not_supported_test("Query '%s' failed, required functionality " \
5594
                             "not supported", command->query);
5595
  }
5596
5597
  if (command->abort_on_error)
5598
    die("query '%s' failed: %d: %s", command->query, err_errno, err_error);
5599
5600
  DBUG_PRINT("info", ("expected_errors.count: %d",
5601
                      command->expected_errors.count));
5602
  for (i= 0 ; (uint) i < command->expected_errors.count ; i++)
5603
  {
5604
    if (((command->expected_errors.err[i].type == ERR_ERRNO) &&
5605
         (command->expected_errors.err[i].code.errnum == err_errno)) ||
5606
        ((command->expected_errors.err[i].type == ERR_SQLSTATE) &&
5607
         (strncmp(command->expected_errors.err[i].code.sqlstate,
5608
                  err_sqlstate, SQLSTATE_LENGTH) == 0)))
5609
    {
5610
      if (!disable_result_log)
5611
      {
5612
        if (command->expected_errors.count == 1)
5613
        {
5614
          /* Only log error if there is one possible error */
5615
          dynstr_append_mem(ds, "ERROR ", 6);
5616
          replace_dynstr_append(ds, err_sqlstate);
5617
          dynstr_append_mem(ds, ": ", 2);
5618
          replace_dynstr_append(ds, err_error);
5619
          dynstr_append_mem(ds,"\n",1);
5620
        }
5621
        /* Don't log error if we may not get an error */
5622
        else if (command->expected_errors.err[0].type == ERR_SQLSTATE ||
5623
                 (command->expected_errors.err[0].type == ERR_ERRNO &&
5624
                  command->expected_errors.err[0].code.errnum != 0))
5625
          dynstr_append(ds,"Got one of the listed errors\n");
5626
      }
5627
      /* OK */
5628
      DBUG_VOID_RETURN;
5629
    }
5630
  }
5631
5632
  DBUG_PRINT("info",("i: %d  expected_errors: %d", i,
5633
                     command->expected_errors.count));
5634
5635
  if (!disable_result_log)
5636
  {
5637
    dynstr_append_mem(ds, "ERROR ",6);
5638
    replace_dynstr_append(ds, err_sqlstate);
5639
    dynstr_append_mem(ds, ": ", 2);
5640
    replace_dynstr_append(ds, err_error);
5641
    dynstr_append_mem(ds, "\n", 1);
5642
  }
5643
5644
  if (i)
5645
  {
5646
    if (command->expected_errors.err[0].type == ERR_ERRNO)
5647
      die("query '%s' failed with wrong errno %d: '%s', instead of %d...",
5648
          command->query, err_errno, err_error,
5649
          command->expected_errors.err[0].code.errnum);
5650
    else
5651
      die("query '%s' failed with wrong sqlstate %s: '%s', instead of %s...",
5652
          command->query, err_sqlstate, err_error,
5653
	  command->expected_errors.err[0].code.sqlstate);
5654
  }
5655
5656
  DBUG_VOID_RETURN;
5657
}
5658
5659
5660
/*
5661
  Handle absence of errors after execution
5662
5663
  SYNOPSIS
5664
  handle_no_error()
5665
  q - context of query
5666
5667
  RETURN VALUE
5668
  error - function will not return
5669
*/
5670
5671
void handle_no_error(struct st_command *command)
5672
{
5673
  DBUG_ENTER("handle_no_error");
5674
5675
  if (command->expected_errors.err[0].type == ERR_ERRNO &&
5676
      command->expected_errors.err[0].code.errnum != 0)
5677
  {
5678
    /* Error code we wanted was != 0, i.e. not an expected success */
5679
    die("query '%s' succeeded - should have failed with errno %d...",
5680
        command->query, command->expected_errors.err[0].code.errnum);
5681
  }
5682
  else if (command->expected_errors.err[0].type == ERR_SQLSTATE &&
5683
           strcmp(command->expected_errors.err[0].code.sqlstate,"00000") != 0)
5684
  {
5685
    /* SQLSTATE we wanted was != "00000", i.e. not an expected success */
5686
    die("query '%s' succeeded - should have failed with sqlstate %s...",
5687
        command->query, command->expected_errors.err[0].code.sqlstate);
5688
  }
5689
5690
  DBUG_VOID_RETURN;
5691
}
5692
5693
5694
/*
5695
  Run query
5696
5697
  SYNPOSIS
5698
    run_query()
5699
     mysql	mysql handle
5700
     command	currrent command pointer
5701
5702
  flags control the phased/stages of query execution to be performed
5703
  if QUERY_SEND_FLAG bit is on, the query will be sent. If QUERY_REAP_FLAG
5704
  is on the result will be read - for regular query, both bits must be on
5705
*/
5706
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
5707
static void run_query(struct st_connection *cn, 
5708
                      struct st_command *command,
5709
                      int flags)
1 by brian
clean slate
5710
{
5711
  DYNAMIC_STRING *ds;
5712
  DYNAMIC_STRING *save_ds= NULL;
5713
  DYNAMIC_STRING ds_result;
5714
  DYNAMIC_STRING ds_sorted;
5715
  DYNAMIC_STRING ds_warnings;
5716
  DYNAMIC_STRING eval_query;
5717
  char *query;
5718
  int query_len;
5719
  DBUG_ENTER("run_query");
5720
5721
  init_dynamic_string(&ds_warnings, NULL, 0, 256);
5722
5723
  /* Scan for warning before sending to server */
5724
  scan_command_for_warnings(command);
5725
5726
  /*
5727
    Evaluate query if this is an eval command
5728
  */
5729
  if (command->type == Q_EVAL)
5730
  {
5731
    init_dynamic_string(&eval_query, "", command->query_len+256, 1024);
5732
    do_eval(&eval_query, command->query, command->end, FALSE);
5733
    query = eval_query.str;
5734
    query_len = eval_query.length;
5735
  }
5736
  else
5737
  {
5738
    query = command->query;
5739
    query_len = strlen(query);
5740
  }
5741
5742
  /*
5743
    When command->require_file is set the output of _this_ query
5744
    should be compared with an already existing file
5745
    Create a temporary dynamic string to contain the output from
5746
    this query.
5747
  */
5748
  if (command->require_file[0])
5749
  {
5750
    init_dynamic_string(&ds_result, "", 1024, 1024);
5751
    ds= &ds_result;
5752
  }
5753
  else
5754
    ds= &ds_res;
5755
5756
  /*
5757
    Log the query into the output buffer
5758
  */
5759
  if (!disable_query_log && (flags & QUERY_SEND_FLAG))
5760
  {
5761
    replace_dynstr_append_mem(ds, query, query_len);
5762
    dynstr_append_mem(ds, delimiter, delimiter_length);
5763
    dynstr_append_mem(ds, "\n", 1);
5764
  }
5765
5766
  if (display_result_sorted)
5767
  {
5768
    /*
5769
       Collect the query output in a separate string
5770
       that can be sorted before it's added to the
5771
       global result string
5772
    */
5773
    init_dynamic_string(&ds_sorted, "", 1024, 1024);
5774
    save_ds= ds; /* Remember original ds */
5775
    ds= &ds_sorted;
5776
  }
5777
5778
  /*
5779
    Always run with normal C API if it's not a complete
5780
    SEND + REAP
5781
  */
41 by Brian Aker
First pass at cleaning up regex from mysqltest.c
5782
  run_query_normal(cn, command, flags, query, query_len,
5783
                   ds, &ds_warnings);
1 by brian
clean slate
5784
5785
  if (display_result_sorted)
5786
  {
5787
    /* Sort the result set and append it to result */
5788
    dynstr_append_sorted(save_ds, &ds_sorted);
5789
    ds= save_ds;
5790
    dynstr_free(&ds_sorted);
5791
  }
5792
5793
  if (command->require_file[0])
5794
  {
5795
    /* A result file was specified for _this_ query
5796
       and the output should be checked against an already
5797
       existing file which has been specified using --require or --result
5798
    */
5799
    check_require(ds, command->require_file);
5800
  }
5801
5802
  dynstr_free(&ds_warnings);
5803
  if (ds == &ds_result)
5804
    dynstr_free(&ds_result);
5805
  if (command->type == Q_EVAL)
5806
    dynstr_free(&eval_query);
5807
  DBUG_VOID_RETURN;
5808
}
5809
5810
/****************************************************************************/
5811
/*
5812
  Functions to detect different SQL statements
5813
*/
5814
5815
void free_re(void)
5816
{
5817
  my_regex_end();
5818
}
5819
5820
/****************************************************************************/
5821
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
5822
static void get_command_type(struct st_command* command)
1 by brian
clean slate
5823
{
5824
  char save;
5825
  uint type;
5826
  DBUG_ENTER("get_command_type");
5827
5828
  if (*command->query == '}')
5829
  {
5830
    command->type = Q_END_BLOCK;
5831
    DBUG_VOID_RETURN;
5832
  }
5833
5834
  save= command->query[command->first_word_len];
5835
  command->query[command->first_word_len]= 0;
5836
  type= find_type(command->query, &command_typelib, 1+2);
5837
  command->query[command->first_word_len]= save;
5838
  if (type > 0)
5839
  {
5840
    command->type=(enum enum_commands) type;		/* Found command */
5841
5842
    /*
5843
      Look for case where "query" was explicitly specified to
5844
      force command being sent to server
5845
    */
5846
    if (type == Q_QUERY)
5847
    {
5848
      /* Skip the "query" part */
5849
      command->query= command->first_argument;
5850
    }
5851
  }
5852
  else
5853
  {
5854
    /* No mysqltest command matched */
5855
5856
    if (command->type != Q_COMMENT_WITH_COMMAND)
5857
    {
5858
      /* A query that will sent to mysqld */
5859
      command->type= Q_QUERY;
5860
    }
5861
    else
5862
    {
5863
      /* -- comment that didn't contain a mysqltest command */
5864
      command->type= Q_COMMENT;
5865
      warning_msg("Suspicious command '--%s' detected, was this intentional? "\
5866
                  "Use # instead of -- to avoid this warning",
5867
                  command->query);
5868
5869
      if (command->first_word_len &&
5870
          strcmp(command->query + command->first_word_len - 1, delimiter) == 0)
5871
      {
5872
        /*
5873
          Detect comment with command using extra delimiter
5874
          Ex --disable_query_log;
5875
          ^ Extra delimiter causing the command
5876
          to be skipped
5877
        */
5878
        save= command->query[command->first_word_len-1];
5879
        command->query[command->first_word_len-1]= 0;
5880
        if (find_type(command->query, &command_typelib, 1+2) > 0)
5881
          die("Extra delimiter \";\" found");
5882
        command->query[command->first_word_len-1]= save;
5883
5884
      }
5885
    }
5886
  }
5887
5888
  /* Set expected error on command */
5889
  memcpy(&command->expected_errors, &saved_expected_errors,
5890
         sizeof(saved_expected_errors));
5891
  DBUG_PRINT("info", ("There are %d expected errors",
5892
                      command->expected_errors.count));
5893
  command->abort_on_error= (command->expected_errors.count == 0 &&
5894
                            abort_on_error);
5895
5896
  DBUG_VOID_RETURN;
5897
}
5898
5899
5900
5901
/*
5902
  Record how many milliseconds it took to execute the test file
5903
  up until the current line and save it in the dynamic string ds_progress.
5904
5905
  The ds_progress will be dumped to <test_name>.progress when
5906
  test run completes
5907
5908
*/
5909
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
5910
static void mark_progress(struct st_command* command __attribute__((unused)),
5911
                          int line)
1 by brian
clean slate
5912
{
5913
  char buf[32], *end;
5914
  ulonglong timer= timer_now();
5915
  if (!progress_start)
5916
    progress_start= timer;
5917
  timer-= progress_start;
5918
5919
  /* Milliseconds since start */
5920
  end= longlong2str(timer, buf, 10);
5921
  dynstr_append_mem(&ds_progress, buf, (int)(end-buf));
5922
  dynstr_append_mem(&ds_progress, "\t", 1);
5923
5924
  /* Parser line number */
5925
  end= int10_to_str(line, buf, 10);
5926
  dynstr_append_mem(&ds_progress, buf, (int)(end-buf));
5927
  dynstr_append_mem(&ds_progress, "\t", 1);
5928
5929
  /* Filename */
5930
  dynstr_append(&ds_progress, cur_file->file_name);
5931
  dynstr_append_mem(&ds_progress, ":", 1);
5932
5933
  /* Line in file */
5934
  end= int10_to_str(cur_file->lineno, buf, 10);
5935
  dynstr_append_mem(&ds_progress, buf, (int)(end-buf));
5936
5937
5938
  dynstr_append_mem(&ds_progress, "\n", 1);
5939
5940
}
5941
5942
5943
int main(int argc, char **argv)
5944
{
5945
  struct st_command *command;
5946
  my_bool q_send_flag= 0, abort_flag= 0;
5947
  uint command_executed= 0, last_command_executed= 0;
5948
  char save_file[FN_REFLEN];
15 by brian
Fix for stat, NETWARE removal
5949
  struct stat res_info;
1 by brian
clean slate
5950
  MY_INIT(argv[0]);
5951
5952
  save_file[0]= 0;
5953
  TMPDIR[0]= 0;
5954
5955
  /* Init expected errors */
5956
  memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
5957
5958
  /* Init connections */
5959
  memset(connections, 0, sizeof(connections));
5960
  connections_end= connections +
5961
    (sizeof(connections)/sizeof(struct st_connection)) - 1;
5962
  next_con= connections + 1;
5963
5964
#ifdef EMBEDDED_LIBRARY
5965
  /* set appropriate stack for the 'query' threads */
5966
  (void) pthread_attr_init(&cn_thd_attrib);
5967
  pthread_attr_setstacksize(&cn_thd_attrib, DEFAULT_THREAD_STACK);
5968
#endif /*EMBEDDED_LIBRARY*/
5969
5970
  /* Init file stack */
5971
  memset(file_stack, 0, sizeof(file_stack));
5972
  file_stack_end=
5973
    file_stack + (sizeof(file_stack)/sizeof(struct st_test_file)) - 1;
5974
  cur_file= file_stack;
5975
5976
  /* Init block stack */
5977
  memset(block_stack, 0, sizeof(block_stack));
5978
  block_stack_end=
5979
    block_stack + (sizeof(block_stack)/sizeof(struct st_block)) - 1;
5980
  cur_block= block_stack;
5981
  cur_block->ok= TRUE; /* Outer block should always be executed */
5982
  cur_block->cmd= cmd_none;
5983
5984
  my_init_dynamic_array(&q_lines, sizeof(struct st_command*), 1024, 1024);
5985
5986
  if (hash_init(&var_hash, charset_info,
5987
                1024, 0, 0, get_var_key, var_free, MYF(0)))
5988
    die("Variable hash initialization failed");
5989
5990
  var_set_string("$MYSQL_SERVER_VERSION", MYSQL_SERVER_VERSION);
5991
5992
  memset(&master_pos, 0, sizeof(master_pos));
5993
5994
  parser.current_line= parser.read_lines= 0;
5995
  memset(&var_reg, 0, sizeof(var_reg));
5996
5997
  init_builtin_echo();
5998
5999
  init_dynamic_string(&ds_res, "", 65536, 65536);
6000
  init_dynamic_string(&ds_progress, "", 0, 2048);
6001
  init_dynamic_string(&ds_warning_messages, "", 0, 2048);
6002
  parse_args(argc, argv);
6003
6004
  DBUG_PRINT("info",("result_file: '%s'",
6005
                     result_file_name ? result_file_name : ""));
6006
  if (mysql_server_init(embedded_server_arg_count,
6007
			embedded_server_args,
6008
			(char**) embedded_server_groups))
6009
    die("Can't initialize MySQL server");
6010
  server_initialized= 1;
6011
  if (cur_file == file_stack && cur_file->file == 0)
6012
  {
6013
    cur_file->file= stdin;
6014
    cur_file->file_name= my_strdup("<stdin>", MYF(MY_WME));
6015
    cur_file->lineno= 1;
6016
  }
6017
  cur_con= connections;
6018
  if (!( mysql_init(&cur_con->mysql)))
6019
    die("Failed in mysql_init()");
6020
  if (opt_compress)
6021
    mysql_options(&cur_con->mysql,MYSQL_OPT_COMPRESS,NullS);
6022
  mysql_options(&cur_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
6023
  mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME,
6024
                charset_info->csname);
6025
  int opt_protocol= MYSQL_PROTOCOL_TCP;
6026
  mysql_options(&cur_con->mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
6027
  if (opt_charsets_dir)
6028
    mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_DIR,
6029
                  opt_charsets_dir);
6030
6031
  if (!(cur_con->name = my_strdup("default", MYF(MY_WME))))
6032
    die("Out of memory");
6033
6034
  safe_connect(&cur_con->mysql, cur_con->name, opt_host, opt_user, opt_pass,
6035
               opt_db, opt_port);
6036
6037
  /* Use all time until exit if no explicit 'start_timer' */
6038
  timer_start= timer_now();
6039
6040
  /*
6041
    Initialize $mysql_errno with -1, so we can
6042
    - distinguish it from valid values ( >= 0 ) and
6043
    - detect if there was never a command sent to the server
6044
  */
6045
  var_set_errno(-1);
6046
6047
  /* Update $mysql_get_server_version to that of current connection */
6048
  var_set_mysql_get_server_version(&cur_con->mysql);
6049
6050
  if (opt_include)
6051
  {
6052
    open_file(opt_include);
6053
  }
6054
6055
  while (!read_command(&command) && !abort_flag)
6056
  {
6057
    int current_line_inc = 1, processed = 0;
6058
    if (command->type == Q_UNKNOWN || command->type == Q_COMMENT_WITH_COMMAND)
6059
      get_command_type(command);
6060
6061
    if (parsing_disabled &&
6062
        command->type != Q_ENABLE_PARSING &&
6063
        command->type != Q_DISABLE_PARSING)
6064
    {
6065
      command->type= Q_COMMENT;
6066
      scan_command_for_warnings(command);
6067
    }
6068
6069
    if (cur_block->ok)
6070
    {
6071
      command->last_argument= command->first_argument;
6072
      processed = 1;
6073
      switch (command->type) {
6074
      case Q_CONNECT:
6075
        do_connect(command);
6076
        break;
6077
      case Q_CONNECTION: select_connection(command); break;
6078
      case Q_DISCONNECT:
6079
      case Q_DIRTY_CLOSE:
6080
	do_close_connection(command); break;
6081
      case Q_ENABLE_QUERY_LOG:   disable_query_log=0; break;
6082
      case Q_DISABLE_QUERY_LOG:  disable_query_log=1; break;
6083
      case Q_ENABLE_ABORT_ON_ERROR:  abort_on_error=1; break;
6084
      case Q_DISABLE_ABORT_ON_ERROR: abort_on_error=0; break;
6085
      case Q_ENABLE_RESULT_LOG:  disable_result_log=0; break;
6086
      case Q_DISABLE_RESULT_LOG: disable_result_log=1; break;
6087
      case Q_ENABLE_WARNINGS:    disable_warnings=0; break;
6088
      case Q_DISABLE_WARNINGS:   disable_warnings=1; break;
6089
      case Q_ENABLE_INFO:        disable_info=0; break;
6090
      case Q_DISABLE_INFO:       disable_info=1; break;
6091
      case Q_ENABLE_METADATA:    display_metadata=1; break;
6092
      case Q_DISABLE_METADATA:   display_metadata=0; break;
6093
      case Q_SOURCE: do_source(command); break;
6094
      case Q_SLEEP: do_sleep(command, 0); break;
6095
      case Q_REAL_SLEEP: do_sleep(command, 1); break;
6096
      case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(command); break;
6097
      case Q_INC: do_modify_var(command, DO_INC); break;
6098
      case Q_DEC: do_modify_var(command, DO_DEC); break;
6099
      case Q_ECHO: do_echo(command); command_executed++; break;
6100
      case Q_SYSTEM: do_system(command); break;
6101
      case Q_REMOVE_FILE: do_remove_file(command); break;
6102
      case Q_MKDIR: do_mkdir(command); break;
6103
      case Q_RMDIR: do_rmdir(command); break;
6104
      case Q_FILE_EXIST: do_file_exist(command); break;
6105
      case Q_WRITE_FILE: do_write_file(command); break;
6106
      case Q_APPEND_FILE: do_append_file(command); break;
6107
      case Q_DIFF_FILES: do_diff_files(command); break;
6108
      case Q_SEND_QUIT: do_send_quit(command); break;
6109
      case Q_CHANGE_USER: do_change_user(command); break;
6110
      case Q_CAT_FILE: do_cat_file(command); break;
6111
      case Q_COPY_FILE: do_copy_file(command); break;
6112
      case Q_CHMOD_FILE: do_chmod_file(command); break;
6113
      case Q_PERL: do_perl(command); break;
6114
      case Q_DELIMITER:
6115
        do_delimiter(command);
6116
	break;
6117
      case Q_DISPLAY_VERTICAL_RESULTS:
6118
        display_result_vertically= TRUE;
6119
        break;
6120
      case Q_DISPLAY_HORIZONTAL_RESULTS:
6121
	display_result_vertically= FALSE;
6122
        break;
6123
      case Q_SORTED_RESULT:
6124
        /*
6125
          Turn on sorting of result set, will be reset after next
6126
          command
6127
        */
6128
	display_result_sorted= TRUE;
6129
        break;
6130
      case Q_LET: do_let(command); break;
6131
      case Q_EVAL_RESULT:
6132
        die("'eval_result' command  is deprecated");
6133
      case Q_EVAL:
6134
      case Q_QUERY_VERTICAL:
6135
      case Q_QUERY_HORIZONTAL:
6136
	if (command->query == command->query_buf)
6137
        {
6138
          /* Skip the first part of command, i.e query_xxx */
6139
	  command->query= command->first_argument;
6140
          command->first_word_len= 0;
6141
        }
6142
	/* fall through */
6143
      case Q_QUERY:
6144
      case Q_REAP:
6145
      {
6146
	my_bool old_display_result_vertically= display_result_vertically;
6147
        /* Default is full query, both reap and send  */
6148
        int flags= QUERY_REAP_FLAG | QUERY_SEND_FLAG;
6149
6150
        if (q_send_flag)
6151
        {
6152
          /* Last command was an empty 'send' */
6153
          flags= QUERY_SEND_FLAG;
6154
          q_send_flag= 0;
6155
        }
6156
        else if (command->type == Q_REAP)
6157
        {
6158
          flags= QUERY_REAP_FLAG;
6159
        }
6160
6161
        /* Check for special property for this query */
6162
        display_result_vertically|= (command->type == Q_QUERY_VERTICAL);
6163
6164
	if (save_file[0])
6165
	{
6166
	  strmake(command->require_file, save_file, sizeof(save_file) - 1);
6167
	  save_file[0]= 0;
6168
	}
6169
	run_query(cur_con, command, flags);
6170
	command_executed++;
6171
        command->last_argument= command->end;
6172
6173
        /* Restore settings */
6174
	display_result_vertically= old_display_result_vertically;
6175
6176
	break;
6177
      }
6178
      case Q_SEND:
6179
        if (!*command->first_argument)
6180
        {
6181
          /*
6182
            This is a send without arguments, it indicates that _next_ query
6183
            should be send only
6184
          */
6185
          q_send_flag= 1;
6186
          break;
6187
        }
6188
6189
        /* Remove "send" if this is first iteration */
6190
	if (command->query == command->query_buf)
6191
	  command->query= command->first_argument;
6192
6193
	/*
6194
	  run_query() can execute a query partially, depending on the flags.
6195
	  QUERY_SEND_FLAG flag without QUERY_REAP_FLAG tells it to just send
6196
          the query and read the result some time later when reap instruction
6197
	  is given on this connection.
6198
        */
6199
	run_query(cur_con, command, QUERY_SEND_FLAG);
6200
	command_executed++;
6201
        command->last_argument= command->end;
6202
	break;
6203
      case Q_REQUIRE:
6204
	do_get_file_name(command, save_file, sizeof(save_file));
6205
	break;
6206
      case Q_ERROR:
6207
        do_get_errcodes(command);
6208
	break;
6209
      case Q_REPLACE:
6210
	do_get_replace(command);
6211
	break;
6212
      case Q_REPLACE_REGEX:
6213
        do_get_replace_regex(command);
6214
        break;
6215
      case Q_REPLACE_COLUMN:
6216
	do_get_replace_column(command);
6217
	break;
6218
      case Q_SAVE_MASTER_POS: do_save_master_pos(); break;
6219
      case Q_SYNC_WITH_MASTER: do_sync_with_master(command); break;
6220
      case Q_SYNC_SLAVE_WITH_MASTER:
6221
      {
6222
	do_save_master_pos();
6223
	if (*command->first_argument)
6224
	  select_connection(command);
6225
	else
6226
	  select_connection_name("slave");
6227
	do_sync_with_master2(0);
6228
	break;
6229
      }
6230
      case Q_COMMENT:				/* Ignore row */
6231
        command->last_argument= command->end;
6232
	break;
6233
      case Q_PING:
6234
	(void) mysql_ping(&cur_con->mysql);
6235
	break;
6236
      case Q_EXEC:
6237
	do_exec(command);
6238
	command_executed++;
6239
	break;
6240
      case Q_START_TIMER:
6241
	/* Overwrite possible earlier start of timer */
6242
	timer_start= timer_now();
6243
	break;
6244
      case Q_END_TIMER:
6245
	/* End timer before ending mysqltest */
6246
	timer_output();
6247
	break;
6248
      case Q_CHARACTER_SET:
6249
	do_set_charset(command);
6250
	break;
6251
      case Q_DISABLE_RECONNECT:
6252
        set_reconnect(&cur_con->mysql, 0);
6253
        break;
6254
      case Q_ENABLE_RECONNECT:
6255
        set_reconnect(&cur_con->mysql, 1);
6256
        break;
6257
      case Q_DISABLE_PARSING:
6258
        if (parsing_disabled == 0)
6259
          parsing_disabled= 1;
6260
        else
6261
          die("Parsing is already disabled");
6262
        break;
6263
      case Q_ENABLE_PARSING:
6264
        /*
6265
          Ensure we don't get parsing_disabled < 0 as this would accidentally
6266
          disable code we don't want to have disabled
6267
        */
6268
        if (parsing_disabled == 1)
6269
          parsing_disabled= 0;
6270
        else
6271
          die("Parsing is already enabled");
6272
        break;
6273
      case Q_DIE:
6274
        /* Abort test with error code and error message */
6275
        die("%s", command->first_argument);
6276
        break;
6277
      case Q_EXIT:
6278
        /* Stop processing any more commands */
6279
        abort_flag= 1;
6280
        break;
6281
      case Q_SKIP:
6282
        abort_not_supported_test("%s", command->first_argument);
6283
        break;
6284
6285
      case Q_RESULT:
6286
        die("result, deprecated command");
6287
        break;
6288
6289
      default:
6290
        processed= 0;
6291
        break;
6292
      }
6293
    }
6294
6295
    if (!processed)
6296
    {
6297
      current_line_inc= 0;
6298
      switch (command->type) {
6299
      case Q_WHILE: do_block(cmd_while, command); break;
6300
      case Q_IF: do_block(cmd_if, command); break;
6301
      case Q_END_BLOCK: do_done(command); break;
6302
      default: current_line_inc = 1; break;
6303
      }
6304
    }
6305
    else
6306
      check_eol_junk(command->last_argument);
6307
6308
    if (command->type != Q_ERROR &&
6309
        command->type != Q_COMMENT)
6310
    {
6311
      /*
6312
        As soon as any non "error" command or comment has been executed,
6313
        the array with expected errors should be cleared
6314
      */
6315
      memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
6316
    }
6317
6318
    if (command_executed != last_command_executed)
6319
    {
6320
      /*
6321
        As soon as any command has been executed,
6322
        the replace structures should be cleared
6323
      */
6324
      free_all_replace();
6325
6326
      /* Also reset "sorted_result" */
6327
      display_result_sorted= FALSE;
6328
    }
6329
    last_command_executed= command_executed;
6330
6331
    parser.current_line += current_line_inc;
6332
    if ( opt_mark_progress )
6333
      mark_progress(command, parser.current_line);
6334
  }
6335
6336
  start_lineno= 0;
6337
6338
  if (parsing_disabled)
6339
    die("Test ended with parsing disabled");
6340
6341
  /*
6342
    The whole test has been executed _sucessfully_.
6343
    Time to compare result or save it to record file.
6344
    The entire output from test is now kept in ds_res.
6345
  */
6346
  if (ds_res.length)
6347
  {
6348
    if (result_file_name)
6349
    {
6350
      /* A result file has been specified */
6351
6352
      if (record)
6353
      {
6354
	/* Recording - dump the output from test to result file */
6355
	str_to_file(result_file_name, ds_res.str, ds_res.length);
6356
      }
6357
      else
6358
      {
6359
	/* Check that the output from test is equal to result file
6360
	   - detect missing result file
6361
	   - detect zero size result file
6362
        */
6363
	check_result(&ds_res);
6364
      }
6365
    }
6366
    else
6367
    {
6368
      /* No result_file_name specified to compare with, print to stdout */
6369
      printf("%s", ds_res.str);
6370
    }
6371
  }
6372
  else
6373
  {
6374
    die("The test didn't produce any output");
6375
  }
6376
6377
  if (!command_executed &&
15 by brian
Fix for stat, NETWARE removal
6378
      result_file_name && !stat(result_file_name, &res_info))
1 by brian
clean slate
6379
  {
6380
    /*
6381
      my_stat() successful on result file. Check if we have not run a
6382
      single query, but we do have a result file that contains data.
6383
      Note that we don't care, if my_stat() fails. For example, for a
6384
      non-existing or non-readable file, we assume it's fine to have
6385
      no query output from the test file, e.g. regarded as no error.
6386
    */
6387
    die("No queries executed but result file found!");
6388
  }
6389
6390
  if ( opt_mark_progress && result_file_name )
6391
    dump_progress();
6392
6393
  /* Dump warning messages */
6394
  if (result_file_name && ds_warning_messages.length)
6395
    dump_warning_messages();
6396
6397
  timer_output();
6398
  /* Yes, if we got this far the test has suceeded! Sakila smiles */
6399
  cleanup_and_exit(0);
6400
  return 0; /* Keep compiler happy too */
6401
}
6402
6403
6404
/*
6405
  A primitive timer that give results in milliseconds if the
6406
  --timer-file=<filename> is given. The timer result is written
6407
  to that file when the result is available. To not confuse
6408
  mysql-test-run with an old obsolete result, we remove the file
6409
  before executing any commands. The time we measure is
6410
6411
  - If no explicit 'start_timer' or 'end_timer' is given in the
6412
  test case, the timer measure how long we execute in mysqltest.
6413
6414
  - If only 'start_timer' is given we measure how long we execute
6415
  from that point until we terminate mysqltest.
6416
6417
  - If only 'end_timer' is given we measure how long we execute
6418
  from that we enter mysqltest to the 'end_timer' is command is
6419
  executed.
6420
6421
  - If both 'start_timer' and 'end_timer' are given we measure
6422
  the time between executing the two commands.
6423
*/
6424
6425
void timer_output(void)
6426
{
6427
  if (timer_file)
6428
  {
6429
    char buf[32], *end;
6430
    ulonglong timer= timer_now() - timer_start;
6431
    end= longlong2str(timer, buf, 10);
6432
    str_to_file(timer_file,buf, (int) (end-buf));
6433
    /* Timer has been written to the file, don't use it anymore */
6434
    timer_file= 0;
6435
  }
6436
}
6437
6438
6439
ulonglong timer_now(void)
6440
{
6441
  return my_micro_time() / 1000;
6442
}
6443
6444
6445
/*
6446
  Get arguments for replace_columns. The syntax is:
6447
  replace-column column_number to_string [column_number to_string ...]
6448
  Where each argument may be quoted with ' or "
6449
  A argument may also be a variable, in which case the value of the
6450
  variable is replaced.
6451
*/
6452
6453
void do_get_replace_column(struct st_command *command)
6454
{
6455
  char *from= command->first_argument;
6456
  char *buff, *start;
6457
  DBUG_ENTER("get_replace_columns");
6458
6459
  free_replace_column();
6460
  if (!*from)
6461
    die("Missing argument in %s", command->query);
6462
6463
  /* Allocate a buffer for results */
6464
  start= buff= my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
6465
  while (*from)
6466
  {
6467
    char *to;
6468
    uint column_number;
6469
6470
    to= get_string(&buff, &from, command);
6471
    if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS)
6472
      die("Wrong column number to replace_column in '%s'", command->query);
6473
    if (!*from)
6474
      die("Wrong number of arguments to replace_column in '%s'", command->query);
6475
    to= get_string(&buff, &from, command);
6476
    my_free(replace_column[column_number-1], MY_ALLOW_ZERO_PTR);
6477
    replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE));
6478
    set_if_bigger(max_replace_column, column_number);
6479
  }
6480
  my_free(start, MYF(0));
6481
  command->last_argument= command->end;
6482
}
6483
6484
6485
void free_replace_column()
6486
{
6487
  uint i;
6488
  for (i=0 ; i < max_replace_column ; i++)
6489
  {
6490
    if (replace_column[i])
6491
    {
6492
      my_free(replace_column[i], 0);
6493
      replace_column[i]= 0;
6494
    }
6495
  }
6496
  max_replace_column= 0;
6497
}
6498
6499
6500
/****************************************************************************/
6501
/*
6502
  Replace functions
6503
*/
6504
6505
/* Definitions for replace result */
6506
6507
typedef struct st_pointer_array {		/* when using array-strings */
6508
  TYPELIB typelib;				/* Pointer to strings */
6509
  uchar	*str;					/* Strings is here */
6510
  int7	*flag;					/* Flag about each var. */
6511
  uint	array_allocs,max_count,length,max_length;
6512
} POINTER_ARRAY;
6513
6514
struct st_replace;
6515
struct st_replace *init_replace(char * *from, char * *to, uint count,
6516
				char * word_end_chars);
6517
int insert_pointer_name(POINTER_ARRAY *pa,char * name);
6518
void replace_strings_append(struct st_replace *rep, DYNAMIC_STRING* ds,
6519
                            const char *from, int len);
6520
void free_pointer_array(POINTER_ARRAY *pa);
6521
6522
struct st_replace *glob_replace;
6523
6524
/*
6525
  Get arguments for replace. The syntax is:
6526
  replace from to [from to ...]
6527
  Where each argument may be quoted with ' or "
6528
  A argument may also be a variable, in which case the value of the
6529
  variable is replaced.
6530
*/
6531
6532
void do_get_replace(struct st_command *command)
6533
{
6534
  uint i;
6535
  char *from= command->first_argument;
6536
  char *buff, *start;
6537
  char word_end_chars[256], *pos;
6538
  POINTER_ARRAY to_array, from_array;
6539
  DBUG_ENTER("get_replace");
6540
6541
  free_replace();
6542
6543
  bzero((char*) &to_array,sizeof(to_array));
6544
  bzero((char*) &from_array,sizeof(from_array));
6545
  if (!*from)
6546
    die("Missing argument in %s", command->query);
6547
  start= buff= my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
6548
  while (*from)
6549
  {
6550
    char *to= buff;
6551
    to= get_string(&buff, &from, command);
6552
    if (!*from)
6553
      die("Wrong number of arguments to replace_result in '%s'",
6554
          command->query);
6555
    insert_pointer_name(&from_array,to);
6556
    to= get_string(&buff, &from, command);
6557
    insert_pointer_name(&to_array,to);
6558
  }
6559
  for (i= 1,pos= word_end_chars ; i < 256 ; i++)
6560
    if (my_isspace(charset_info,i))
6561
      *pos++= i;
6562
  *pos=0;					/* End pointer */
6563
  if (!(glob_replace= init_replace((char**) from_array.typelib.type_names,
6564
				  (char**) to_array.typelib.type_names,
6565
				  (uint) from_array.typelib.count,
6566
				  word_end_chars)))
6567
    die("Can't initialize replace from '%s'", command->query);
6568
  free_pointer_array(&from_array);
6569
  free_pointer_array(&to_array);
6570
  my_free(start, MYF(0));
6571
  command->last_argument= command->end;
6572
  DBUG_VOID_RETURN;
6573
}
6574
6575
6576
void free_replace()
6577
{
6578
  DBUG_ENTER("free_replace");
6579
  if (glob_replace)
6580
  {
6581
    my_free(glob_replace,MYF(0));
6582
    glob_replace=0;
6583
  }
6584
  DBUG_VOID_RETURN;
6585
}
6586
6587
6588
typedef struct st_replace {
6589
  my_bool found;
6590
  struct st_replace *next[256];
6591
} REPLACE;
6592
6593
typedef struct st_replace_found {
6594
  my_bool found;
6595
  char *replace_string;
6596
  uint to_offset;
6597
  int from_offset;
6598
} REPLACE_STRING;
6599
6600
6601
void replace_strings_append(REPLACE *rep, DYNAMIC_STRING* ds,
6602
                            const char *str,
6603
                            int len __attribute__((unused)))
6604
{
6605
  register REPLACE *rep_pos;
6606
  register REPLACE_STRING *rep_str;
6607
  const char *start, *from;
6608
  DBUG_ENTER("replace_strings_append");
6609
6610
  start= from= str;
6611
  rep_pos=rep+1;
6612
  for (;;)
6613
  {
6614
    /* Loop through states */
6615
    DBUG_PRINT("info", ("Looping through states"));
6616
    while (!rep_pos->found)
6617
      rep_pos= rep_pos->next[(uchar) *from++];
6618
6619
    /* Does this state contain a string to be replaced */
6620
    if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string)
6621
    {
6622
      /* No match found */
6623
      dynstr_append_mem(ds, start, from - start - 1);
6624
      DBUG_PRINT("exit", ("Found no more string to replace, appended: %s", start));
6625
      DBUG_VOID_RETURN;
6626
    }
6627
6628
    /* Found a string that needs to be replaced */
6629
    DBUG_PRINT("info", ("found: %d, to_offset: %d, from_offset: %d, string: %s",
6630
                        rep_str->found, rep_str->to_offset,
6631
                        rep_str->from_offset, rep_str->replace_string));
6632
6633
    /* Append part of original string before replace string */
6634
    dynstr_append_mem(ds, start, (from - rep_str->to_offset) - start);
6635
6636
    /* Append replace string */
6637
    dynstr_append_mem(ds, rep_str->replace_string,
6638
                      strlen(rep_str->replace_string));
6639
6640
    if (!*(from-=rep_str->from_offset) && rep_pos->found != 2)
6641
    {
6642
      /* End of from string */
6643
      DBUG_PRINT("exit", ("Found end of from string"));
6644
      DBUG_VOID_RETURN;
6645
    }
6646
    DBUG_ASSERT(from <= str+len);
6647
    start= from;
6648
    rep_pos=rep;
6649
  }
6650
}
6651
6652
6653
/*
6654
  Regex replace  functions
6655
*/
6656
6657
6658
/* Stores regex substitutions */
6659
6660
struct st_regex
6661
{
6662
  char* pattern; /* Pattern to be replaced */
6663
  char* replace; /* String or expression to replace the pattern with */
6664
  int icase; /* true if the match is case insensitive */
6665
};
6666
6667
struct st_replace_regex
6668
{
6669
  DYNAMIC_ARRAY regex_arr; /* stores a list of st_regex subsitutions */
6670
6671
  /*
6672
    Temporary storage areas for substitutions. To reduce unnessary copying
6673
    and memory freeing/allocation, we pre-allocate two buffers, and alternate
6674
    their use, one for input/one for output, the roles changing on the next
6675
    st_regex substition. At the end of substitutions  buf points to the
6676
    one containing the final result.
6677
  */
6678
  char* buf;
6679
  char* even_buf;
6680
  char* odd_buf;
6681
  int even_buf_len;
6682
  int odd_buf_len;
6683
};
6684
6685
struct st_replace_regex *glob_replace_regex= 0;
6686
6687
int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace,
6688
                char *string, int icase);
6689
6690
6691
6692
/*
6693
  Finds the next (non-escaped) '/' in the expression.
6694
  (If the character '/' is needed, it can be escaped using '\'.)
6695
*/
6696
6697
#define PARSE_REGEX_ARG                         \
6698
  while (p < expr_end)                          \
6699
  {                                             \
6700
    char c= *p;                                 \
6701
    if (c == '/')                               \
6702
    {                                           \
6703
      if (last_c == '\\')                       \
6704
      {                                         \
6705
        buf_p[-1]= '/';                         \
6706
      }                                         \
6707
      else                                      \
6708
      {                                         \
6709
        *buf_p++ = 0;                           \
6710
        break;                                  \
6711
      }                                         \
6712
    }                                           \
6713
    else                                        \
6714
      *buf_p++ = c;                             \
6715
                                                \
6716
    last_c= c;                                  \
6717
    p++;                                        \
6718
  }                                             \
6719
                                                \
6720
/*
6721
  Initializes the regular substitution expression to be used in the
6722
  result output of test.
6723
6724
  Returns: st_replace_regex struct with pairs of substitutions
6725
*/
6726
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
6727
static struct st_replace_regex* init_replace_regex(char* expr)
1 by brian
clean slate
6728
{
6729
  struct st_replace_regex* res;
6730
  char* buf,*expr_end;
6731
  char* p;
6732
  char* buf_p;
6733
  uint expr_len= strlen(expr);
6734
  char last_c = 0;
6735
  struct st_regex reg;
6736
6737
  /* my_malloc() will die on fail with MY_FAE */
6738
  res=(struct st_replace_regex*)my_malloc(
6739
                                          sizeof(*res)+expr_len ,MYF(MY_FAE+MY_WME));
6740
  my_init_dynamic_array(&res->regex_arr,sizeof(struct st_regex),128,128);
6741
6742
  buf= (char*)res + sizeof(*res);
6743
  expr_end= expr + expr_len;
6744
  p= expr;
6745
  buf_p= buf;
6746
6747
  /* for each regexp substitution statement */
6748
  while (p < expr_end)
6749
  {
6750
    bzero(&reg,sizeof(reg));
6751
    /* find the start of the statement */
6752
    while (p < expr_end)
6753
    {
6754
      if (*p == '/')
6755
        break;
6756
      p++;
6757
    }
6758
6759
    if (p == expr_end || ++p == expr_end)
6760
    {
6761
      if (res->regex_arr.elements)
6762
        break;
6763
      else
6764
        goto err;
6765
    }
6766
    /* we found the start */
6767
    reg.pattern= buf_p;
6768
6769
    /* Find first argument -- pattern string to be removed */
6770
    PARSE_REGEX_ARG
6771
6772
      if (p == expr_end || ++p == expr_end)
6773
        goto err;
6774
6775
    /* buf_p now points to the replacement pattern terminated with \0 */
6776
    reg.replace= buf_p;
6777
6778
    /* Find second argument -- replace string to replace pattern */
6779
    PARSE_REGEX_ARG
6780
6781
      if (p == expr_end)
6782
        goto err;
6783
6784
    /* skip the ending '/' in the statement */
6785
    p++;
6786
6787
    /* Check if we should do matching case insensitive */
6788
    if (p < expr_end && *p == 'i')
6789
      reg.icase= 1;
6790
6791
    /* done parsing the statement, now place it in regex_arr */
6792
    if (insert_dynamic(&res->regex_arr,(uchar*) &reg))
6793
      die("Out of memory");
6794
  }
6795
  res->odd_buf_len= res->even_buf_len= 8192;
6796
  res->even_buf= (char*)my_malloc(res->even_buf_len,MYF(MY_WME+MY_FAE));
6797
  res->odd_buf= (char*)my_malloc(res->odd_buf_len,MYF(MY_WME+MY_FAE));
6798
  res->buf= res->even_buf;
6799
6800
  return res;
6801
6802
err:
6803
  my_free(res,0);
6804
  die("Error parsing replace_regex \"%s\"", expr);
6805
  return 0;
6806
}
6807
6808
/*
6809
  Execute all substitutions on val.
6810
6811
  Returns: true if substituition was made, false otherwise
6812
  Side-effect: Sets r->buf to be the buffer with all substitutions done.
6813
6814
  IN:
6815
  struct st_replace_regex* r
6816
  char* val
6817
  Out:
6818
  struct st_replace_regex* r
6819
  r->buf points at the resulting buffer
6820
  r->even_buf and r->odd_buf might have been reallocated
6821
  r->even_buf_len and r->odd_buf_len might have been changed
6822
6823
  TODO:  at some point figure out if there is a way to do everything
6824
  in one pass
6825
*/
6826
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
6827
static int multi_reg_replace(struct st_replace_regex* r,char* val)
1 by brian
clean slate
6828
{
6829
  uint i;
6830
  char* in_buf, *out_buf;
6831
  int* buf_len_p;
6832
6833
  in_buf= val;
6834
  out_buf= r->even_buf;
6835
  buf_len_p= &r->even_buf_len;
6836
  r->buf= 0;
6837
6838
  /* For each substitution, do the replace */
6839
  for (i= 0; i < r->regex_arr.elements; i++)
6840
  {
6841
    struct st_regex re;
6842
    char* save_out_buf= out_buf;
6843
6844
    get_dynamic(&r->regex_arr,(uchar*)&re,i);
6845
6846
    if (!reg_replace(&out_buf, buf_len_p, re.pattern, re.replace,
6847
                     in_buf, re.icase))
6848
    {
6849
      /* if the buffer has been reallocated, make adjustements */
6850
      if (save_out_buf != out_buf)
6851
      {
6852
        if (save_out_buf == r->even_buf)
6853
          r->even_buf= out_buf;
6854
        else
6855
          r->odd_buf= out_buf;
6856
      }
6857
6858
      r->buf= out_buf;
6859
      if (in_buf == val)
6860
        in_buf= r->odd_buf;
6861
6862
      swap_variables(char*,in_buf,out_buf);
6863
6864
      buf_len_p= (out_buf == r->even_buf) ? &r->even_buf_len :
6865
        &r->odd_buf_len;
6866
    }
6867
  }
6868
6869
  return (r->buf == 0);
6870
}
6871
6872
/*
6873
  Parse the regular expression to be used in all result files
6874
  from now on.
6875
6876
  The syntax is --replace_regex /from/to/i /from/to/i ...
6877
  i means case-insensitive match. If omitted, the match is
6878
  case-sensitive
6879
6880
*/
6881
void do_get_replace_regex(struct st_command *command)
6882
{
6883
  char *expr= command->first_argument;
6884
  free_replace_regex();
6885
  if (!(glob_replace_regex=init_replace_regex(expr)))
6886
    die("Could not init replace_regex");
6887
  command->last_argument= command->end;
6888
}
6889
6890
void free_replace_regex()
6891
{
6892
  if (glob_replace_regex)
6893
  {
6894
    delete_dynamic(&glob_replace_regex->regex_arr);
6895
    my_free(glob_replace_regex->even_buf,MYF(MY_ALLOW_ZERO_PTR));
6896
    my_free(glob_replace_regex->odd_buf,MYF(MY_ALLOW_ZERO_PTR));
6897
    my_free(glob_replace_regex,MYF(0));
6898
    glob_replace_regex=0;
6899
  }
6900
}
6901
6902
6903
6904
/*
6905
  auxiluary macro used by reg_replace
6906
  makes sure the result buffer has sufficient length
6907
*/
6908
#define SECURE_REG_BUF   if (buf_len < need_buf_len)                    \
6909
  {                                                                     \
6910
    int off= res_p - buf;                                               \
6911
    buf= (char*)my_realloc(buf,need_buf_len,MYF(MY_WME+MY_FAE));        \
6912
    res_p= buf + off;                                                   \
6913
    buf_len= need_buf_len;                                              \
6914
  }                                                                     \
6915
                                                                        \
6916
/*
6917
  Performs a regex substitution
6918
6919
  IN:
6920
6921
  buf_p - result buffer pointer. Will change if reallocated
6922
  buf_len_p - result buffer length. Will change if the buffer is reallocated
6923
  pattern - regexp pattern to match
6924
  replace - replacement expression
6925
  string - the string to perform substituions in
6926
  icase - flag, if set to 1 the match is case insensitive
6927
*/
6928
int reg_replace(char** buf_p, int* buf_len_p, char *pattern,
6929
                char *replace, char *string, int icase)
6930
{
6931
  my_regex_t r;
6932
  my_regmatch_t *subs;
6933
  char *replace_end;
6934
  char *buf= *buf_p;
6935
  int len;
6936
  int buf_len, need_buf_len;
6937
  int cflags= REG_EXTENDED;
6938
  int err_code;
6939
  char *res_p,*str_p,*str_end;
6940
6941
  buf_len= *buf_len_p;
6942
  len= strlen(string);
6943
  str_end= string + len;
6944
6945
  /* start with a buffer of a reasonable size that hopefully will not
6946
     need to be reallocated
6947
  */
6948
  need_buf_len= len * 2 + 1;
6949
  res_p= buf;
6950
6951
  SECURE_REG_BUF
6952
6953
  if (icase)
6954
    cflags|= REG_ICASE;
6955
6956
  if ((err_code= my_regcomp(&r,pattern,cflags,&my_charset_latin1)))
6957
  {
6958
    check_regerr(&r,err_code);
6959
    return 1;
6960
  }
6961
6962
  subs= (my_regmatch_t*)my_malloc(sizeof(my_regmatch_t) * (r.re_nsub+1),
6963
                                  MYF(MY_WME+MY_FAE));
6964
6965
  *res_p= 0;
6966
  str_p= string;
6967
  replace_end= replace + strlen(replace);
6968
6969
  /* for each pattern match instance perform a replacement */
6970
  while (!err_code)
6971
  {
6972
    /* find the match */
6973
    err_code= my_regexec(&r,str_p, r.re_nsub+1, subs,
6974
                         (str_p == string) ? REG_NOTBOL : 0);
6975
6976
    /* if regular expression error (eg. bad syntax, or out of memory) */
6977
    if (err_code && err_code != REG_NOMATCH)
6978
    {
6979
      check_regerr(&r,err_code);
6980
      my_regfree(&r);
6981
      return 1;
6982
    }
6983
6984
    /* if match found */
6985
    if (!err_code)
6986
    {
6987
      char* expr_p= replace;
6988
      int c;
6989
6990
      /*
6991
        we need at least what we have so far in the buffer + the part
6992
        before this match
6993
      */
6994
      need_buf_len= (res_p - buf) + (int) subs[0].rm_so;
6995
6996
      /* on this pass, calculate the memory for the result buffer */
6997
      while (expr_p < replace_end)
6998
      {
6999
        int back_ref_num= -1;
7000
        c= *expr_p;
7001
7002
        if (c == '\\' && expr_p + 1 < replace_end)
7003
        {
7004
          back_ref_num= (int) (expr_p[1] - '0');
7005
        }
7006
7007
        /* found a valid back_ref (eg. \1)*/
7008
        if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub)
7009
        {
7010
          regoff_t start_off, end_off;
7011
          if ((start_off=subs[back_ref_num].rm_so) > -1 &&
7012
              (end_off=subs[back_ref_num].rm_eo) > -1)
7013
          {
7014
            need_buf_len += (int) (end_off - start_off);
7015
          }
7016
          expr_p += 2;
7017
        }
7018
        else
7019
        {
7020
          expr_p++;
7021
          need_buf_len++;
7022
        }
7023
      }
7024
      need_buf_len++;
7025
      /*
7026
        now that we know the size of the buffer,
7027
        make sure it is big enough
7028
      */
7029
      SECURE_REG_BUF
7030
7031
        /* copy the pre-match part */
7032
        if (subs[0].rm_so)
7033
        {
7034
          memcpy(res_p, str_p, (size_t) subs[0].rm_so);
7035
          res_p+= subs[0].rm_so;
7036
        }
7037
7038
      expr_p= replace;
7039
7040
      /* copy the match and expand back_refs */
7041
      while (expr_p < replace_end)
7042
      {
7043
        int back_ref_num= -1;
7044
        c= *expr_p;
7045
7046
        if (c == '\\' && expr_p + 1 < replace_end)
7047
        {
7048
          back_ref_num= expr_p[1] - '0';
7049
        }
7050
7051
        if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub)
7052
        {
7053
          regoff_t start_off, end_off;
7054
          if ((start_off=subs[back_ref_num].rm_so) > -1 &&
7055
              (end_off=subs[back_ref_num].rm_eo) > -1)
7056
          {
7057
            int block_len= (int) (end_off - start_off);
7058
            memcpy(res_p,str_p + start_off, block_len);
7059
            res_p += block_len;
7060
          }
7061
          expr_p += 2;
7062
        }
7063
        else
7064
        {
7065
          *res_p++ = *expr_p++;
7066
        }
7067
      }
7068
7069
      /* handle the post-match part */
7070
      if (subs[0].rm_so == subs[0].rm_eo)
7071
      {
7072
        if (str_p + subs[0].rm_so >= str_end)
7073
          break;
7074
        str_p += subs[0].rm_eo ;
7075
        *res_p++ = *str_p++;
7076
      }
7077
      else
7078
      {
7079
        str_p += subs[0].rm_eo;
7080
      }
7081
    }
7082
    else /* no match this time, just copy the string as is */
7083
    {
7084
      int left_in_str= str_end-str_p;
7085
      need_buf_len= (res_p-buf) + left_in_str;
7086
      SECURE_REG_BUF
7087
        memcpy(res_p,str_p,left_in_str);
7088
      res_p += left_in_str;
7089
      str_p= str_end;
7090
    }
7091
  }
7092
  my_free(subs, MYF(0));
7093
  my_regfree(&r);
7094
  *res_p= 0;
7095
  *buf_p= buf;
7096
  *buf_len_p= buf_len;
7097
  return 0;
7098
}
7099
7100
7101
#ifndef WORD_BIT
7102
#define WORD_BIT (8*sizeof(uint))
7103
#endif
7104
7105
#define SET_MALLOC_HUNC 64
7106
#define LAST_CHAR_CODE 259
7107
7108
typedef struct st_rep_set {
7109
  uint	*bits;				/* Pointer to used sets */
7110
  short next[LAST_CHAR_CODE];		/* Pointer to next sets */
7111
  uint	found_len;			/* Best match to date */
7112
  int	found_offset;
7113
  uint	table_offset;
7114
  uint	size_of_bits;			/* For convinience */
7115
} REP_SET;
7116
7117
typedef struct st_rep_sets {
7118
  uint		count;			/* Number of sets */
7119
  uint		extra;			/* Extra sets in buffer */
7120
  uint		invisible;		/* Sets not chown */
7121
  uint		size_of_bits;
7122
  REP_SET	*set,*set_buffer;
7123
  uint		*bit_buffer;
7124
} REP_SETS;
7125
7126
typedef struct st_found_set {
7127
  uint table_offset;
7128
  int found_offset;
7129
} FOUND_SET;
7130
7131
typedef struct st_follow {
7132
  int chr;
7133
  uint table_offset;
7134
  uint len;
7135
} FOLLOWS;
7136
7137
7138
int init_sets(REP_SETS *sets,uint states);
7139
REP_SET *make_new_set(REP_SETS *sets);
7140
void make_sets_invisible(REP_SETS *sets);
7141
void free_last_set(REP_SETS *sets);
7142
void free_sets(REP_SETS *sets);
7143
void internal_set_bit(REP_SET *set, uint bit);
7144
void internal_clear_bit(REP_SET *set, uint bit);
7145
void or_bits(REP_SET *to,REP_SET *from);
7146
void copy_bits(REP_SET *to,REP_SET *from);
7147
int cmp_bits(REP_SET *set1,REP_SET *set2);
7148
int get_next_bit(REP_SET *set,uint lastpos);
7149
int find_set(REP_SETS *sets,REP_SET *find);
7150
int find_found(FOUND_SET *found_set,uint table_offset,
7151
               int found_offset);
7152
uint start_at_word(char * pos);
7153
uint end_of_word(char * pos);
7154
7155
static uint found_sets=0;
7156
7157
53.2.4 by Monty Taylor
Changes so that client/ builds cleanly with no warnings.
7158
static uint replace_len(char * str)
1 by brian
clean slate
7159
{
7160
  uint len=0;
7161
  while (*str)
7162
  {
7163
    if (str[0] == '\\' && str[1])
7164
      str++;
7165
    str++;
7166
    len++;
7167
  }
7168
  return len;
7169
}
7170
7171
/* Init a replace structure for further calls */
7172
7173
REPLACE *init_replace(char * *from, char * *to,uint count,
7174
		      char * word_end_chars)
7175
{
7176
  static const int SPACE_CHAR= 256;
7177
  static const int START_OF_LINE= 257;
7178
  static const int END_OF_LINE= 258;
7179
7180
  uint i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr;
7181
  int used_sets,chr,default_state;
7182
  char used_chars[LAST_CHAR_CODE],is_word_end[256];
7183
  char * pos, *to_pos, **to_array;
7184
  REP_SETS sets;
7185
  REP_SET *set,*start_states,*word_states,*new_set;
7186
  FOLLOWS *follow,*follow_ptr;
7187
  REPLACE *replace;
7188
  FOUND_SET *found_set;
7189
  REPLACE_STRING *rep_str;
7190
  DBUG_ENTER("init_replace");
7191
7192
  /* Count number of states */
7193
  for (i=result_len=max_length=0 , states=2 ; i < count ; i++)
7194
  {
7195
    len=replace_len(from[i]);
7196
    if (!len)
7197
    {
7198
      errno=EINVAL;
7199
      DBUG_RETURN(0);
7200
    }
7201
    states+=len+1;
7202
    result_len+=(uint) strlen(to[i])+1;
7203
    if (len > max_length)
7204
      max_length=len;
7205
  }
7206
  bzero((char*) is_word_end,sizeof(is_word_end));
7207
  for (i=0 ; word_end_chars[i] ; i++)
7208
    is_word_end[(uchar) word_end_chars[i]]=1;
7209
7210
  if (init_sets(&sets,states))
7211
    DBUG_RETURN(0);
7212
  found_sets=0;
7213
  if (!(found_set= (FOUND_SET*) my_malloc(sizeof(FOUND_SET)*max_length*count,
7214
					  MYF(MY_WME))))
7215
  {
7216
    free_sets(&sets);
7217
    DBUG_RETURN(0);
7218
  }
7219
  VOID(make_new_set(&sets));			/* Set starting set */
7220
  make_sets_invisible(&sets);			/* Hide previus sets */
7221
  used_sets=-1;
7222
  word_states=make_new_set(&sets);		/* Start of new word */
7223
  start_states=make_new_set(&sets);		/* This is first state */
7224
  if (!(follow=(FOLLOWS*) my_malloc((states+2)*sizeof(FOLLOWS),MYF(MY_WME))))
7225
  {
7226
    free_sets(&sets);
7227
    my_free(found_set,MYF(0));
7228
    DBUG_RETURN(0);
7229
  }
7230
7231
  /* Init follow_ptr[] */
7232
  for (i=0, states=1, follow_ptr=follow+1 ; i < count ; i++)
7233
  {
7234
    if (from[i][0] == '\\' && from[i][1] == '^')
7235
    {
7236
      internal_set_bit(start_states,states+1);
7237
      if (!from[i][2])
7238
      {
7239
	start_states->table_offset=i;
7240
	start_states->found_offset=1;
7241
      }
7242
    }
7243
    else if (from[i][0] == '\\' && from[i][1] == '$')
7244
    {
7245
      internal_set_bit(start_states,states);
7246
      internal_set_bit(word_states,states);
7247
      if (!from[i][2] && start_states->table_offset == (uint) ~0)
7248
      {
7249
	start_states->table_offset=i;
7250
	start_states->found_offset=0;
7251
      }
7252
    }
7253
    else
7254
    {
7255
      internal_set_bit(word_states,states);
7256
      if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2]))
7257
	internal_set_bit(start_states,states+1);
7258
      else
7259
	internal_set_bit(start_states,states);
7260
    }
7261
    for (pos=from[i], len=0; *pos ; pos++)
7262
    {
7263
      if (*pos == '\\' && *(pos+1))
7264
      {
7265
	pos++;
7266
	switch (*pos) {
7267
	case 'b':
7268
	  follow_ptr->chr = SPACE_CHAR;
7269
	  break;
7270
	case '^':
7271
	  follow_ptr->chr = START_OF_LINE;
7272
	  break;
7273
	case '$':
7274
	  follow_ptr->chr = END_OF_LINE;
7275
	  break;
7276
	case 'r':
7277
	  follow_ptr->chr = '\r';
7278
	  break;
7279
	case 't':
7280
	  follow_ptr->chr = '\t';
7281
	  break;
7282
	case 'v':
7283
	  follow_ptr->chr = '\v';
7284
	  break;
7285
	default:
7286
	  follow_ptr->chr = (uchar) *pos;
7287
	  break;
7288
	}
7289
      }
7290
      else
7291
	follow_ptr->chr= (uchar) *pos;
7292
      follow_ptr->table_offset=i;
7293
      follow_ptr->len= ++len;
7294
      follow_ptr++;
7295
    }
7296
    follow_ptr->chr=0;
7297
    follow_ptr->table_offset=i;
7298
    follow_ptr->len=len;
7299
    follow_ptr++;
7300
    states+=(uint) len+1;
7301
  }
7302
7303
7304
  for (set_nr=0,pos=0 ; set_nr < sets.count ; set_nr++)
7305
  {
7306
    set=sets.set+set_nr;
7307
    default_state= 0;				/* Start from beginning */
7308
7309
    /* If end of found-string not found or start-set with current set */
7310
7311
    for (i= (uint) ~0; (i=get_next_bit(set,i)) ;)
7312
    {
7313
      if (!follow[i].chr)
7314
      {
7315
	if (! default_state)
7316
	  default_state= find_found(found_set,set->table_offset,
7317
				    set->found_offset+1);
7318
      }
7319
    }
7320
    copy_bits(sets.set+used_sets,set);		/* Save set for changes */
7321
    if (!default_state)
7322
      or_bits(sets.set+used_sets,sets.set);	/* Can restart from start */
7323
7324
    /* Find all chars that follows current sets */
7325
    bzero((char*) used_chars,sizeof(used_chars));
7326
    for (i= (uint) ~0; (i=get_next_bit(sets.set+used_sets,i)) ;)
7327
    {
7328
      used_chars[follow[i].chr]=1;
7329
      if ((follow[i].chr == SPACE_CHAR && !follow[i+1].chr &&
7330
	   follow[i].len > 1) || follow[i].chr == END_OF_LINE)
7331
	used_chars[0]=1;
7332
    }
7333
7334
    /* Mark word_chars used if \b is in state */
7335
    if (used_chars[SPACE_CHAR])
7336
      for (pos= word_end_chars ; *pos ; pos++)
7337
	used_chars[(int) (uchar) *pos] = 1;
7338
7339
    /* Handle other used characters */
7340
    for (chr= 0 ; chr < 256 ; chr++)
7341
    {
7342
      if (! used_chars[chr])
7343
	set->next[chr]= chr ? default_state : -1;
7344
      else
7345
      {
7346
	new_set=make_new_set(&sets);
7347
	set=sets.set+set_nr;			/* if realloc */
7348
	new_set->table_offset=set->table_offset;
7349
	new_set->found_len=set->found_len;
7350
	new_set->found_offset=set->found_offset+1;
7351
	found_end=0;
7352
7353
	for (i= (uint) ~0 ; (i=get_next_bit(sets.set+used_sets,i)) ; )
7354
	{
7355
	  if (!follow[i].chr || follow[i].chr == chr ||
7356
	      (follow[i].chr == SPACE_CHAR &&
7357
	       (is_word_end[chr] ||
7358
		(!chr && follow[i].len > 1 && ! follow[i+1].chr))) ||
7359
	      (follow[i].chr == END_OF_LINE && ! chr))
7360
	  {
7361
	    if ((! chr || (follow[i].chr && !follow[i+1].chr)) &&
7362
		follow[i].len > found_end)
7363
	      found_end=follow[i].len;
7364
	    if (chr && follow[i].chr)
7365
	      internal_set_bit(new_set,i+1);		/* To next set */
7366
	    else
7367
	      internal_set_bit(new_set,i);
7368
	  }
7369
	}
7370
	if (found_end)
7371
	{
7372
	  new_set->found_len=0;			/* Set for testing if first */
7373
	  bits_set=0;
7374
	  for (i= (uint) ~0; (i=get_next_bit(new_set,i)) ;)
7375
	  {
7376
	    if ((follow[i].chr == SPACE_CHAR ||
7377
		 follow[i].chr == END_OF_LINE) && ! chr)
7378
	      bit_nr=i+1;
7379
	    else
7380
	      bit_nr=i;
7381
	    if (follow[bit_nr-1].len < found_end ||
7382
		(new_set->found_len &&
7383
		 (chr == 0 || !follow[bit_nr].chr)))
7384
	      internal_clear_bit(new_set,i);
7385
	    else
7386
	    {
7387
	      if (chr == 0 || !follow[bit_nr].chr)
7388
	      {					/* best match  */
7389
		new_set->table_offset=follow[bit_nr].table_offset;
7390
		if (chr || (follow[i].chr == SPACE_CHAR ||
7391
			    follow[i].chr == END_OF_LINE))
7392
		  new_set->found_offset=found_end;	/* New match */
7393
		new_set->found_len=found_end;
7394
	      }
7395
	      bits_set++;
7396
	    }
7397
	  }
7398
	  if (bits_set == 1)
7399
	  {
7400
	    set->next[chr] = find_found(found_set,
7401
					new_set->table_offset,
7402
					new_set->found_offset);
7403
	    free_last_set(&sets);
7404
	  }
7405
	  else
7406
	    set->next[chr] = find_set(&sets,new_set);
7407
	}
7408
	else
7409
	  set->next[chr] = find_set(&sets,new_set);
7410
      }
7411
    }
7412
  }
7413
7414
  /* Alloc replace structure for the replace-state-machine */
7415
7416
  if ((replace=(REPLACE*) my_malloc(sizeof(REPLACE)*(sets.count)+
7417
				    sizeof(REPLACE_STRING)*(found_sets+1)+
7418
				    sizeof(char *)*count+result_len,
7419
				    MYF(MY_WME | MY_ZEROFILL))))
7420
  {
7421
    rep_str=(REPLACE_STRING*) (replace+sets.count);
7422
    to_array= (char **) (rep_str+found_sets+1);
7423
    to_pos=(char *) (to_array+count);
7424
    for (i=0 ; i < count ; i++)
7425
    {
7426
      to_array[i]=to_pos;
7427
      to_pos=strmov(to_pos,to[i])+1;
7428
    }
7429
    rep_str[0].found=1;
7430
    rep_str[0].replace_string=0;
7431
    for (i=1 ; i <= found_sets ; i++)
7432
    {
7433
      pos=from[found_set[i-1].table_offset];
7434
      rep_str[i].found= !bcmp((const uchar*) pos,
7435
			      (const uchar*) "\\^", 3) ? 2 : 1;
7436
      rep_str[i].replace_string=to_array[found_set[i-1].table_offset];
7437
      rep_str[i].to_offset=found_set[i-1].found_offset-start_at_word(pos);
7438
      rep_str[i].from_offset=found_set[i-1].found_offset-replace_len(pos)+
7439
	end_of_word(pos);
7440
    }
7441
    for (i=0 ; i < sets.count ; i++)
7442
    {
7443
      for (j=0 ; j < 256 ; j++)
7444
	if (sets.set[i].next[j] >= 0)
7445
	  replace[i].next[j]=replace+sets.set[i].next[j];
7446
	else
7447
	  replace[i].next[j]=(REPLACE*) (rep_str+(-sets.set[i].next[j]-1));
7448
    }
7449
  }
7450
  my_free(follow,MYF(0));
7451
  free_sets(&sets);
7452
  my_free(found_set,MYF(0));
7453
  DBUG_PRINT("exit",("Replace table has %d states",sets.count));
7454
  DBUG_RETURN(replace);
7455
}
7456
7457
7458
int init_sets(REP_SETS *sets,uint states)
7459
{
7460
  bzero((char*) sets,sizeof(*sets));
7461
  sets->size_of_bits=((states+7)/8);
7462
  if (!(sets->set_buffer=(REP_SET*) my_malloc(sizeof(REP_SET)*SET_MALLOC_HUNC,
7463
					      MYF(MY_WME))))
7464
    return 1;
7465
  if (!(sets->bit_buffer=(uint*) my_malloc(sizeof(uint)*sets->size_of_bits*
7466
					   SET_MALLOC_HUNC,MYF(MY_WME))))
7467
  {
7468
    my_free(sets->set,MYF(0));
7469
    return 1;
7470
  }
7471
  return 0;
7472
}
7473
7474
/* Make help sets invisible for nicer codeing */
7475
7476
void make_sets_invisible(REP_SETS *sets)
7477
{
7478
  sets->invisible=sets->count;
7479
  sets->set+=sets->count;
7480
  sets->count=0;
7481
}
7482
7483
REP_SET *make_new_set(REP_SETS *sets)
7484
{
7485
  uint i,count,*bit_buffer;
7486
  REP_SET *set;
7487
  if (sets->extra)
7488
  {
7489
    sets->extra--;
7490
    set=sets->set+ sets->count++;
7491
    bzero((char*) set->bits,sizeof(uint)*sets->size_of_bits);
7492
    bzero((char*) &set->next[0],sizeof(set->next[0])*LAST_CHAR_CODE);
7493
    set->found_offset=0;
7494
    set->found_len=0;
7495
    set->table_offset= (uint) ~0;
7496
    set->size_of_bits=sets->size_of_bits;
7497
    return set;
7498
  }
7499
  count=sets->count+sets->invisible+SET_MALLOC_HUNC;
7500
  if (!(set=(REP_SET*) my_realloc((uchar*) sets->set_buffer,
7501
                                  sizeof(REP_SET)*count,
7502
				  MYF(MY_WME))))
7503
    return 0;
7504
  sets->set_buffer=set;
7505
  sets->set=set+sets->invisible;
7506
  if (!(bit_buffer=(uint*) my_realloc((uchar*) sets->bit_buffer,
7507
				      (sizeof(uint)*sets->size_of_bits)*count,
7508
				      MYF(MY_WME))))
7509
    return 0;
7510
  sets->bit_buffer=bit_buffer;
7511
  for (i=0 ; i < count ; i++)
7512
  {
7513
    sets->set_buffer[i].bits=bit_buffer;
7514
    bit_buffer+=sets->size_of_bits;
7515
  }
7516
  sets->extra=SET_MALLOC_HUNC;
7517
  return make_new_set(sets);
7518
}
7519
7520
void free_last_set(REP_SETS *sets)
7521
{
7522
  sets->count--;
7523
  sets->extra++;
7524
  return;
7525
}
7526
7527
void free_sets(REP_SETS *sets)
7528
{
7529
  my_free(sets->set_buffer,MYF(0));
7530
  my_free(sets->bit_buffer,MYF(0));
7531
  return;
7532
}
7533
7534
void internal_set_bit(REP_SET *set, uint bit)
7535
{
7536
  set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
7537
  return;
7538
}
7539
7540
void internal_clear_bit(REP_SET *set, uint bit)
7541
{
7542
  set->bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT));
7543
  return;
7544
}
7545
7546
7547
void or_bits(REP_SET *to,REP_SET *from)
7548
{
7549
  register uint i;
7550
  for (i=0 ; i < to->size_of_bits ; i++)
7551
    to->bits[i]|=from->bits[i];
7552
  return;
7553
}
7554
7555
void copy_bits(REP_SET *to,REP_SET *from)
7556
{
7557
  memcpy((uchar*) to->bits,(uchar*) from->bits,
7558
	 (size_t) (sizeof(uint) * to->size_of_bits));
7559
}
7560
7561
int cmp_bits(REP_SET *set1,REP_SET *set2)
7562
{
7563
  return bcmp((uchar*) set1->bits,(uchar*) set2->bits,
7564
	      sizeof(uint) * set1->size_of_bits);
7565
}
7566
7567
7568
/* Get next set bit from set. */
7569
7570
int get_next_bit(REP_SET *set,uint lastpos)
7571
{
7572
  uint pos,*start,*end,bits;
7573
7574
  start=set->bits+ ((lastpos+1) / WORD_BIT);
7575
  end=set->bits + set->size_of_bits;
7576
  bits=start[0] & ~((1 << ((lastpos+1) % WORD_BIT)) -1);
7577
7578
  while (! bits && ++start < end)
7579
    bits=start[0];
7580
  if (!bits)
7581
    return 0;
7582
  pos=(uint) (start-set->bits)*WORD_BIT;
7583
  while (! (bits & 1))
7584
  {
7585
    bits>>=1;
7586
    pos++;
7587
  }
7588
  return pos;
7589
}
7590
7591
/* find if there is a same set in sets. If there is, use it and
7592
   free given set, else put in given set in sets and return its
7593
   position */
7594
7595
int find_set(REP_SETS *sets,REP_SET *find)
7596
{
7597
  uint i;
7598
  for (i=0 ; i < sets->count-1 ; i++)
7599
  {
7600
    if (!cmp_bits(sets->set+i,find))
7601
    {
7602
      free_last_set(sets);
7603
      return i;
7604
    }
7605
  }
7606
  return i;				/* return new postion */
7607
}
7608
7609
/* find if there is a found_set with same table_offset & found_offset
7610
   If there is return offset to it, else add new offset and return pos.
7611
   Pos returned is -offset-2 in found_set_structure because it is
7612
   saved in set->next and set->next[] >= 0 points to next set and
7613
   set->next[] == -1 is reserved for end without replaces.
7614
*/
7615
7616
int find_found(FOUND_SET *found_set,uint table_offset, int found_offset)
7617
{
7618
  int i;
7619
  for (i=0 ; (uint) i < found_sets ; i++)
7620
    if (found_set[i].table_offset == table_offset &&
7621
	found_set[i].found_offset == found_offset)
7622
      return -i-2;
7623
  found_set[i].table_offset=table_offset;
7624
  found_set[i].found_offset=found_offset;
7625
  found_sets++;
7626
  return -i-2;				/* return new postion */
7627
}
7628
7629
/* Return 1 if regexp starts with \b or ends with \b*/
7630
7631
uint start_at_word(char * pos)
7632
{
7633
  return (((!bcmp((const uchar*) pos, (const uchar*) "\\b",2) && pos[2]) ||
7634
           !bcmp((const uchar*) pos, (const uchar*) "\\^", 2)) ? 1 : 0);
7635
}
7636
7637
uint end_of_word(char * pos)
7638
{
7639
  char * end=strend(pos);
7640
  return ((end > pos+2 && !bcmp((const uchar*) end-2,
7641
                                (const uchar*) "\\b", 2)) ||
7642
	  (end >= pos+2 && !bcmp((const uchar*) end-2,
7643
                                (const uchar*) "\\$",2))) ? 1 : 0;
7644
}
7645
7646
/****************************************************************************
7647
 * Handle replacement of strings
7648
 ****************************************************************************/
7649
7650
#define PC_MALLOC		256	/* Bytes for pointers */
7651
#define PS_MALLOC		512	/* Bytes for data */
7652
7653
int insert_pointer_name(POINTER_ARRAY *pa,char * name)
7654
{
7655
  uint i,length,old_count;
7656
  uchar *new_pos;
7657
  const char **new_array;
7658
  DBUG_ENTER("insert_pointer_name");
7659
7660
  if (! pa->typelib.count)
7661
  {
7662
    if (!(pa->typelib.type_names=(const char **)
7663
	  my_malloc(((PC_MALLOC-MALLOC_OVERHEAD)/
7664
		     (sizeof(char *)+sizeof(*pa->flag))*
7665
		     (sizeof(char *)+sizeof(*pa->flag))),MYF(MY_WME))))
7666
      DBUG_RETURN(-1);
7667
    if (!(pa->str= (uchar*) my_malloc((uint) (PS_MALLOC-MALLOC_OVERHEAD),
7668
				     MYF(MY_WME))))
7669
    {
7670
      my_free((char*) pa->typelib.type_names,MYF(0));
7671
      DBUG_RETURN (-1);
7672
    }
7673
    pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(uchar*)+
7674
					       sizeof(*pa->flag));
7675
    pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
7676
    pa->length=0;
7677
    pa->max_length=PS_MALLOC-MALLOC_OVERHEAD;
7678
    pa->array_allocs=1;
7679
  }
7680
  length=(uint) strlen(name)+1;
7681
  if (pa->length+length >= pa->max_length)
7682
  {
7683
    if (!(new_pos= (uchar*) my_realloc((uchar*) pa->str,
7684
				      (uint) (pa->max_length+PS_MALLOC),
7685
				      MYF(MY_WME))))
7686
      DBUG_RETURN(1);
7687
    if (new_pos != pa->str)
7688
    {
7689
      my_ptrdiff_t diff=PTR_BYTE_DIFF(new_pos,pa->str);
7690
      for (i=0 ; i < pa->typelib.count ; i++)
7691
	pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff,
7692
					      char*);
7693
      pa->str=new_pos;
7694
    }
7695
    pa->max_length+=PS_MALLOC;
7696
  }
7697
  if (pa->typelib.count >= pa->max_count-1)
7698
  {
7699
    int len;
7700
    pa->array_allocs++;
7701
    len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD);
7702
    if (!(new_array=(const char **) my_realloc((uchar*) pa->typelib.type_names,
7703
					       (uint) len/
7704
                                               (sizeof(uchar*)+sizeof(*pa->flag))*
7705
                                               (sizeof(uchar*)+sizeof(*pa->flag)),
7706
                                               MYF(MY_WME))))
7707
      DBUG_RETURN(1);
7708
    pa->typelib.type_names=new_array;
7709
    old_count=pa->max_count;
7710
    pa->max_count=len/(sizeof(uchar*) + sizeof(*pa->flag));
7711
    pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
7712
    memcpy((uchar*) pa->flag,(char *) (pa->typelib.type_names+old_count),
7713
	   old_count*sizeof(*pa->flag));
7714
  }
7715
  pa->flag[pa->typelib.count]=0;			/* Reset flag */
7716
  pa->typelib.type_names[pa->typelib.count++]= (char*) pa->str+pa->length;
7717
  pa->typelib.type_names[pa->typelib.count]= NullS;	/* Put end-mark */
7718
  VOID(strmov((char*) pa->str+pa->length,name));
7719
  pa->length+=length;
7720
  DBUG_RETURN(0);
7721
} /* insert_pointer_name */
7722
7723
7724
/* free pointer array */
7725
7726
void free_pointer_array(POINTER_ARRAY *pa)
7727
{
7728
  if (pa->typelib.count)
7729
  {
7730
    pa->typelib.count=0;
7731
    my_free((char*) pa->typelib.type_names,MYF(0));
7732
    pa->typelib.type_names=0;
7733
    my_free(pa->str,MYF(0));
7734
  }
7735
} /* free_pointer_array */
7736
7737
7738
/* Functions that uses replace and replace_regex */
7739
7740
/* Append the string to ds, with optional replace */
7741
void replace_dynstr_append_mem(DYNAMIC_STRING *ds,
7742
                               const char *val, int len)
7743
{
7744
  if (glob_replace_regex)
7745
  {
7746
    /* Regex replace */
7747
    if (!multi_reg_replace(glob_replace_regex, (char*)val))
7748
    {
7749
      val= glob_replace_regex->buf;
7750
      len= strlen(val);
7751
    }
7752
  }
7753
7754
  if (glob_replace)
7755
  {
7756
    /* Normal replace */
7757
    replace_strings_append(glob_replace, ds, val, len);
7758
  }
7759
  else
7760
    dynstr_append_mem(ds, val, len);
7761
}
7762
7763
7764
/* Append zero-terminated string to ds, with optional replace */
7765
void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val)
7766
{
7767
  replace_dynstr_append_mem(ds, val, strlen(val));
7768
}
7769
7770
/* Append uint to ds, with optional replace */
7771
void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val)
7772
{
7773
  char buff[22]; /* This should be enough for any int */
7774
  char *end= longlong10_to_str(val, buff, 10);
7775
  replace_dynstr_append_mem(ds, buff, end - buff);
7776
}
7777
7778
7779
7780
/*
7781
  Build a list of pointer to each line in ds_input, sort
7782
  the list and use the sorted list to append the strings
7783
  sorted to the output ds
7784
7785
  SYNOPSIS
7786
  dynstr_append_sorted
7787
  ds - string where the sorted output will be appended
7788
  ds_input - string to be sorted
7789
7790
*/
7791
7792
static int comp_lines(const char **a, const char **b)
7793
{
7794
  return (strcmp(*a,*b));
7795
}
7796
7797
void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING *ds_input)
7798
{
7799
  unsigned i;
7800
  char *start= ds_input->str;
7801
  DYNAMIC_ARRAY lines;
7802
  DBUG_ENTER("dynstr_append_sorted");
7803
7804
  if (!*start)
7805
    DBUG_VOID_RETURN;  /* No input */
7806
7807
  my_init_dynamic_array(&lines, sizeof(const char*), 32, 32);
7808
7809
  /* First line is result header, skip past it */
7810
  while (*start && *start != '\n')
7811
    start++;
7812
  start++; /* Skip past \n */
7813
  dynstr_append_mem(ds, ds_input->str, start - ds_input->str);
7814
7815
  /* Insert line(s) in array */
7816
  while (*start)
7817
  {
7818
    char* line_end= (char*)start;
7819
7820
    /* Find end of line */
7821
    while (*line_end && *line_end != '\n')
7822
      line_end++;
7823
    *line_end= 0;
7824
7825
    /* Insert pointer to the line in array */
7826
    if (insert_dynamic(&lines, (uchar*) &start))
7827
      die("Out of memory inserting lines to sort");
7828
7829
    start= line_end+1;
7830
  }
7831
7832
  /* Sort array */
7833
  qsort(lines.buffer, lines.elements,
7834
        sizeof(char**), (qsort_cmp)comp_lines);
7835
7836
  /* Create new result */
7837
  for (i= 0; i < lines.elements ; i++)
7838
  {
7839
    const char **line= dynamic_element(&lines, i, const char**);
7840
    dynstr_append(ds, *line);
7841
    dynstr_append(ds, "\n");
7842
  }
7843
7844
  delete_dynamic(&lines);
7845
  DBUG_VOID_RETURN;
7846
}