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