~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to client/mysqltest.c

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

Show diffs side-by-side

added added

removed removed

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