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