1/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
2 Copyright (c) 2009, 2017, MariaDB
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
16
17/*
18 mysqltest
19
20 Tool used for executing a .test file
21
22 See the "MySQL Test framework manual" for more information
23 http://dev.mysql.com/doc/mysqltest/en/index.html
24
25 Please keep the test framework tools identical in all versions!
26
27 Written by:
28 Sasha Pachev <sasha@mysql.com>
29 Matt Wagner <matt@mysql.com>
30 Monty
31 Jani
32 Holyfoot
33 And many others
34*/
35
36#define MTEST_VERSION "3.5"
37
38#include "client_priv.h"
39#include <mysql_version.h>
40#include <mysqld_error.h>
41#include <sql_common.h>
42#include <m_ctype.h>
43#include <my_dir.h>
44#include <hash.h>
45#include <stdarg.h>
46#include <violite.h>
47#define PCRE_STATIC 1 /* Important on Windows */
48#include "pcreposix.h" /* pcreposix regex library */
49#ifdef HAVE_SYS_WAIT_H
50#include <sys/wait.h>
51#endif
52#ifdef _WIN32
53#include <direct.h>
54#endif
55#include <signal.h>
56#include <my_stacktrace.h>
57
58#include <welcome_copyright_notice.h> // ORACLE_WELCOME_COPYRIGHT_NOTICE
59
60#ifdef _WIN32
61#include <crtdbg.h>
62#define SIGNAL_FMT "exception 0x%x"
63#else
64#define SIGNAL_FMT "signal %d"
65#endif
66
67#include <my_context.h>
68static my_bool non_blocking_api_enabled= 0;
69#if !defined(EMBEDDED_LIBRARY) && !defined(MY_CONTEXT_DISABLE)
70#define WRAP_NONBLOCK_ENABLED non_blocking_api_enabled
71#include "../tests/nonblock-wrappers.h"
72#endif
73
74/* Use cygwin for --exec and --system before 5.0 */
75#if MYSQL_VERSION_ID < 50000
76#define USE_CYGWIN
77#endif
78
79#define MAX_VAR_NAME_LENGTH 256
80#define MAX_COLUMNS 256
81#define MAX_EMBEDDED_SERVER_ARGS 64
82#define MAX_DELIMITER_LENGTH 16
83#define DEFAULT_MAX_CONN 64
84
85#define DIE_BUFF_SIZE 256*1024
86
87/* Flags controlling send and reap */
88#define QUERY_SEND_FLAG 1
89#define QUERY_REAP_FLAG 2
90
91#define QUERY_PRINT_ORIGINAL_FLAG 4
92
93#ifndef HAVE_SETENV
94static int setenv(const char *name, const char *value, int overwrite);
95#endif
96
97C_MODE_START
98static sig_handler signal_handler(int sig);
99static my_bool get_one_option(int optid, const struct my_option *,
100 char *argument);
101C_MODE_END
102
103enum {
104 OPT_LOG_DIR=OPT_MAX_CLIENT_OPTION, OPT_RESULT_FORMAT_VERSION
105};
106
107static int record= 0, opt_sleep= -1;
108static char *opt_db= 0, *opt_pass= 0;
109const char *opt_user= 0, *opt_host= 0, *unix_sock= 0, *opt_basedir= "./";
110static char *shared_memory_base_name=0;
111const char *opt_logdir= "";
112const char *opt_prologue= 0, *opt_charsets_dir;
113static int opt_port= 0;
114static int opt_max_connect_retries;
115static int opt_result_format_version;
116static int opt_max_connections= DEFAULT_MAX_CONN;
117static int error_count= 0;
118static my_bool opt_compress= 0, silent= 0, verbose= 0;
119static my_bool debug_info_flag= 0, debug_check_flag= 0;
120static my_bool tty_password= 0;
121static my_bool opt_mark_progress= 0;
122static my_bool ps_protocol= 0, ps_protocol_enabled= 0;
123static my_bool sp_protocol= 0, sp_protocol_enabled= 0;
124static my_bool view_protocol= 0, view_protocol_enabled= 0;
125static my_bool cursor_protocol= 0, cursor_protocol_enabled= 0;
126static my_bool parsing_disabled= 0;
127static my_bool display_result_vertically= FALSE, display_result_lower= FALSE,
128 display_metadata= FALSE, display_result_sorted= FALSE;
129static my_bool disable_query_log= 0, disable_result_log= 0;
130static my_bool disable_connect_log= 0;
131static my_bool disable_warnings= 0, disable_column_names= 0;
132static my_bool prepare_warnings_enabled= 0;
133static my_bool disable_info= 1;
134static my_bool abort_on_error= 1, opt_continue_on_error= 0;
135static my_bool server_initialized= 0;
136static my_bool is_windows= 0;
137static char **default_argv;
138static const char *load_default_groups[]=
139{ "mysqltest", "client", "client-server", "client-mariadb", 0 };
140static char line_buffer[MAX_DELIMITER_LENGTH], *line_buffer_pos= line_buffer;
141
142/* Info on properties that can be set with --enable_X and --disable_X */
143
144struct property {
145 my_bool *var; /* Actual variable */
146 my_bool set; /* Has been set for ONE command */
147 my_bool old; /* If set, thus is the old value */
148 my_bool reverse; /* Varible is true if disabled */
149 const char *env_name; /* Env. variable name */
150};
151
152static struct property prop_list[] = {
153 { &abort_on_error, 0, 1, 0, "$ENABLED_ABORT_ON_ERROR" },
154 { &disable_connect_log, 0, 1, 1, "$ENABLED_CONNECT_LOG" },
155 { &disable_info, 0, 1, 1, "$ENABLED_INFO" },
156 { &display_metadata, 0, 0, 0, "$ENABLED_METADATA" },
157 { &ps_protocol_enabled, 0, 0, 0, "$ENABLED_PS_PROTOCOL" },
158 { &disable_query_log, 0, 0, 1, "$ENABLED_QUERY_LOG" },
159 { &disable_result_log, 0, 0, 1, "$ENABLED_RESULT_LOG" },
160 { &disable_warnings, 0, 0, 1, "$ENABLED_WARNINGS" }
161};
162
163static my_bool once_property= FALSE;
164
165enum enum_prop {
166 P_ABORT= 0,
167 P_CONNECT,
168 P_INFO,
169 P_META,
170 P_PS,
171 P_QUERY,
172 P_RESULT,
173 P_WARN,
174 P_MAX
175};
176
177static uint start_lineno= 0; /* Start line of current command */
178static uint my_end_arg= 0;
179
180/* Number of lines of the result to include in failure report */
181static uint opt_tail_lines= 0;
182
183static uint opt_connect_timeout= 0;
184static uint opt_wait_for_pos_timeout= 0;
185
186static char delimiter[MAX_DELIMITER_LENGTH]= ";";
187static size_t delimiter_length= 1;
188
189static char TMPDIR[FN_REFLEN];
190static char global_subst_from[200];
191static char global_subst_to[200];
192static char *global_subst= NULL;
193static MEM_ROOT require_file_root;
194static const my_bool my_true= 1;
195static const my_bool my_false= 0;
196
197/* Block stack */
198enum block_cmd {
199 cmd_none,
200 cmd_if,
201 cmd_while
202};
203
204struct st_block
205{
206 int line; /* Start line of block */
207 my_bool ok; /* Should block be executed */
208 enum block_cmd cmd; /* Command owning the block */
209 char delim[MAX_DELIMITER_LENGTH]; /* Delimiter before block */
210};
211
212static struct st_block block_stack[32];
213static struct st_block *cur_block, *block_stack_end;
214
215/* Open file stack */
216struct st_test_file
217{
218 FILE* file;
219 char *file_name;
220 uint lineno; /* Current line in file */
221};
222
223static struct st_test_file file_stack[16];
224static struct st_test_file* cur_file;
225static struct st_test_file* file_stack_end;
226
227static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */
228
229static const char *embedded_server_groups[]=
230{
231 "server",
232 "embedded",
233 "mysqltest_SERVER",
234 NullS
235};
236
237static int embedded_server_arg_count=0;
238static char *embedded_server_args[MAX_EMBEDDED_SERVER_ARGS];
239
240/*
241 Timer related variables
242 See the timer_output() definition for details
243*/
244static char *timer_file = NULL;
245static ulonglong timer_start;
246static void timer_output(void);
247static ulonglong timer_now(void);
248
249
250static ulong connection_retry_sleep= 100000; /* Microseconds */
251
252static const char *opt_plugin_dir;
253static const char *opt_suite_dir, *opt_overlay_dir;
254static size_t suite_dir_len, overlay_dir_len;
255
256/* Precompiled re's */
257static regex_t ps_re; /* the query can be run using PS protocol */
258static regex_t sp_re; /* the query can be run as a SP */
259static regex_t view_re; /* the query can be run as a view*/
260
261static void init_re(void);
262static int match_re(regex_t *, char *);
263static void free_re(void);
264
265static char *get_string(char **to_ptr, char **from_ptr,
266 struct st_command *command);
267static int replace(DYNAMIC_STRING *ds_str,
268 const char *search_str, size_t search_len,
269 const char *replace_str, size_t replace_len);
270
271static uint opt_protocol=0;
272
273DYNAMIC_ARRAY q_lines;
274
275#include "sslopt-vars.h"
276
277struct Parser
278{
279 int read_lines,current_line;
280} parser;
281
282struct MasterPos
283{
284 char file[FN_REFLEN];
285 ulong pos;
286} master_pos;
287
288/* if set, all results are concated and compared against this file */
289const char *result_file_name= 0;
290
291typedef struct
292{
293 char *name;
294 size_t name_len;
295 char *str_val;
296 size_t str_val_len;
297 int int_val;
298 size_t alloced_len;
299 bool int_dirty; /* do not update string if int is updated until first read */
300 bool is_int;
301 bool alloced;
302} VAR;
303
304/*Perl/shell-like variable registers */
305VAR var_reg[10];
306
307HASH var_hash;
308
309struct st_connection
310{
311 MYSQL *mysql;
312 /* Used when creating views and sp, to avoid implicit commit */
313 MYSQL* util_mysql;
314 char *name;
315 size_t name_len;
316 MYSQL_STMT* stmt;
317 /* Set after send to disallow other queries before reap */
318 my_bool pending;
319
320#ifdef EMBEDDED_LIBRARY
321 pthread_t tid;
322 const char *cur_query;
323 int cur_query_len;
324 int command, result;
325 pthread_mutex_t query_mutex;
326 pthread_cond_t query_cond;
327 pthread_mutex_t result_mutex;
328 pthread_cond_t result_cond;
329 int query_done;
330 my_bool has_thread;
331#endif /*EMBEDDED_LIBRARY*/
332};
333
334struct st_connection *connections= NULL;
335struct st_connection* cur_con= NULL, *next_con, *connections_end;
336
337/*
338 List of commands in mysqltest
339 Must match the "command_names" array
340 Add new commands before Q_UNKNOWN!
341*/
342enum enum_commands {
343 Q_CONNECTION=1, Q_QUERY,
344 Q_CONNECT, Q_SLEEP, Q_REAL_SLEEP,
345 Q_INC, Q_DEC,
346 Q_SOURCE, Q_DISCONNECT,
347 Q_LET, Q_ECHO,
348 Q_WHILE, Q_END_BLOCK,
349 Q_SYSTEM, Q_RESULT,
350 Q_REQUIRE, Q_SAVE_MASTER_POS,
351 Q_SYNC_WITH_MASTER,
352 Q_SYNC_SLAVE_WITH_MASTER,
353 Q_ERROR,
354 Q_SEND, Q_REAP,
355 Q_DIRTY_CLOSE, Q_REPLACE, Q_REPLACE_COLUMN,
356 Q_PING, Q_EVAL,
357 Q_EVALP,
358 Q_EVAL_RESULT,
359 Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
360 Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
361 Q_ENABLE_CONNECT_LOG, Q_DISABLE_CONNECT_LOG,
362 Q_WAIT_FOR_SLAVE_TO_STOP,
363 Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
364 Q_ENABLE_INFO, Q_DISABLE_INFO,
365 Q_ENABLE_METADATA, Q_DISABLE_METADATA,
366 Q_ENABLE_COLUMN_NAMES, Q_DISABLE_COLUMN_NAMES,
367 Q_EXEC, Q_DELIMITER,
368 Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR,
369 Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
370 Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, Q_SORTED_RESULT,
371 Q_LOWERCASE,
372 Q_START_TIMER, Q_END_TIMER,
373 Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL,
374 Q_ENABLE_NON_BLOCKING_API, Q_DISABLE_NON_BLOCKING_API,
375 Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT,
376 Q_IF,
377 Q_DISABLE_PARSING, Q_ENABLE_PARSING,
378 Q_REPLACE_REGEX, Q_REMOVE_FILE, Q_FILE_EXIST,
379 Q_WRITE_FILE, Q_COPY_FILE, Q_PERL, Q_DIE, Q_EXIT, Q_SKIP,
380 Q_CHMOD_FILE, Q_APPEND_FILE, Q_CAT_FILE, Q_DIFF_FILES,
381 Q_SEND_QUIT, Q_CHANGE_USER, Q_MKDIR, Q_RMDIR,
382 Q_LIST_FILES, Q_LIST_FILES_WRITE_FILE, Q_LIST_FILES_APPEND_FILE,
383 Q_SEND_SHUTDOWN, Q_SHUTDOWN_SERVER,
384 Q_RESULT_FORMAT_VERSION,
385 Q_MOVE_FILE, Q_REMOVE_FILES_WILDCARD, Q_SEND_EVAL,
386 Q_ENABLE_PREPARE_WARNINGS, Q_DISABLE_PREPARE_WARNINGS,
387 Q_UNKNOWN, /* Unknown command. */
388 Q_COMMENT, /* Comments, ignored. */
389 Q_COMMENT_WITH_COMMAND,
390 Q_EMPTY_LINE
391};
392
393
394const char *command_names[]=
395{
396 "connection",
397 "query",
398 "connect",
399 "sleep",
400 "real_sleep",
401 "inc",
402 "dec",
403 "source",
404 "disconnect",
405 "let",
406 "echo",
407 "while",
408 "end",
409 "system",
410 "result",
411 "require",
412 "save_master_pos",
413 "sync_with_master",
414 "sync_slave_with_master",
415 "error",
416 "send",
417 "reap",
418 "dirty_close",
419 "replace_result",
420 "replace_column",
421 "ping",
422 "eval",
423 "evalp",
424 "eval_result",
425 /* Enable/disable that the _query_ is logged to result file */
426 "enable_query_log",
427 "disable_query_log",
428 /* Enable/disable that the _result_ from a query is logged to result file */
429 "enable_result_log",
430 "disable_result_log",
431 "enable_connect_log",
432 "disable_connect_log",
433 "wait_for_slave_to_stop",
434 "enable_warnings",
435 "disable_warnings",
436 "enable_info",
437 "disable_info",
438 "enable_metadata",
439 "disable_metadata",
440 "enable_column_names",
441 "disable_column_names",
442 "exec",
443 "delimiter",
444 "disable_abort_on_error",
445 "enable_abort_on_error",
446 "vertical_results",
447 "horizontal_results",
448 "query_vertical",
449 "query_horizontal",
450 "sorted_result",
451 "lowercase_result",
452 "start_timer",
453 "end_timer",
454 "character_set",
455 "disable_ps_protocol",
456 "enable_ps_protocol",
457 "enable_non_blocking_api",
458 "disable_non_blocking_api",
459 "disable_reconnect",
460 "enable_reconnect",
461 "if",
462 "disable_parsing",
463 "enable_parsing",
464 "replace_regex",
465 "remove_file",
466 "file_exists",
467 "write_file",
468 "copy_file",
469 "perl",
470 "die",
471
472 /* Don't execute any more commands, compare result */
473 "exit",
474 "skip",
475 "chmod",
476 "append_file",
477 "cat_file",
478 "diff_files",
479 "send_quit",
480 "change_user",
481 "mkdir",
482 "rmdir",
483 "list_files",
484 "list_files_write_file",
485 "list_files_append_file",
486 "send_shutdown",
487 "shutdown_server",
488 "result_format",
489 "move_file",
490 "remove_files_wildcard",
491 "send_eval",
492 "enable_prepare_warnings",
493 "disable_prepare_warnings",
494
495 0
496};
497
498
499/*
500 The list of error codes to --error are stored in an internal array of
501 structs. This struct can hold numeric SQL error codes, error names or
502 SQLSTATE codes as strings. The element next to the last active element
503 in the list is set to type ERR_EMPTY. When an SQL statement returns an
504 error, we use this list to check if this is an expected error.
505*/
506enum match_err_type
507{
508 ERR_EMPTY= 0,
509 ERR_ERRNO,
510 ERR_SQLSTATE
511};
512
513struct st_match_err
514{
515 enum match_err_type type;
516 union
517 {
518 uint errnum;
519 char sqlstate[SQLSTATE_LENGTH+1]; /* \0 terminated string */
520 } code;
521};
522
523struct st_expected_errors
524{
525 struct st_match_err err[12];
526 uint count;
527};
528static struct st_expected_errors saved_expected_errors;
529
530struct st_command
531{
532 char *query, *query_buf,*first_argument,*last_argument,*end;
533 DYNAMIC_STRING content;
534 DYNAMIC_STRING eval_query;
535 int first_word_len, query_len;
536 my_bool abort_on_error, used_replace;
537 struct st_expected_errors expected_errors;
538 char *require_file;
539 enum enum_commands type;
540};
541
542TYPELIB command_typelib= {array_elements(command_names),"",
543 command_names, 0};
544
545DYNAMIC_STRING ds_res;
546/* Points to ds_warning in run_query, so it can be freed */
547DYNAMIC_STRING *ds_warn= 0;
548struct st_command *curr_command= 0;
549
550char builtin_echo[FN_REFLEN];
551
552struct st_replace_regex
553{
554DYNAMIC_ARRAY regex_arr; /* stores a list of st_regex subsitutions */
555
556/*
557Temporary storage areas for substitutions. To reduce unnessary copying
558and memory freeing/allocation, we pre-allocate two buffers, and alternate
559their use, one for input/one for output, the roles changing on the next
560st_regex substition. At the end of substitutions buf points to the
561one containing the final result.
562*/
563char* buf;
564char* even_buf;
565char* odd_buf;
566int even_buf_len;
567int odd_buf_len;
568};
569
570struct st_replace_regex *glob_replace_regex= 0;
571
572struct st_replace;
573struct st_replace *glob_replace= 0;
574void replace_strings_append(struct st_replace *rep, DYNAMIC_STRING* ds,
575const char *from);
576
577ATTRIBUTE_NORETURN
578static void cleanup_and_exit(int exit_code);
579
580ATTRIBUTE_NORETURN
581void really_die(const char *msg);
582void report_or_die(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
583ATTRIBUTE_NORETURN ATTRIBUTE_FORMAT(printf, 1, 2)
584void die(const char *fmt, ...);
585static void make_error_message(char *buf, size_t len, const char *fmt, va_list args);
586ATTRIBUTE_NORETURN ATTRIBUTE_FORMAT(printf, 1, 2)
587void abort_not_supported_test(const char *fmt, ...);
588void verbose_msg(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
589void log_msg(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
590
591VAR* var_from_env(const char *, const char *);
592VAR* var_init(VAR* v, const char *name, size_t name_len, const char *val, size_t val_len);
593VAR* var_get(const char *var_name, const char** var_name_end,
594 my_bool raw, my_bool ignore_not_existing);
595void eval_expr(VAR* v, const char *p, const char** p_end,
596 bool open_end=false, bool do_eval=true);
597my_bool match_delimiter(int c, const char *delim, size_t length);
598void dump_result_to_reject_file(char *buf, int size);
599void dump_warning_messages();
600
601void do_eval(DYNAMIC_STRING *query_eval, const char *query,
602 const char *query_end, my_bool pass_through_escape_chars);
603void str_to_file(const char *fname, char *str, size_t size);
604void str_to_file2(const char *fname, char *str, size_t size, my_bool append);
605
606void fix_win_paths(char *val, size_t len);
607const char *get_errname_from_code (uint error_code);
608int multi_reg_replace(struct st_replace_regex* r,char* val);
609
610#ifdef _WIN32
611void free_tmp_sh_file();
612void free_win_path_patterns();
613#endif
614
615
616/* For replace_column */
617static char *replace_column[MAX_COLUMNS];
618static uint max_replace_column= 0;
619void do_get_replace_column(struct st_command*);
620void free_replace_column();
621
622/* For replace */
623void do_get_replace(struct st_command *command);
624void free_replace();
625
626/* For replace_regex */
627void do_get_replace_regex(struct st_command *command);
628void free_replace_regex();
629
630/* Used by sleep */
631void check_eol_junk_line(const char *eol);
632
633void free_all_replace(){
634 free_replace();
635 free_replace_regex();
636 free_replace_column();
637}
638
639void var_set_int(const char* name, int value);
640
641
642class LogFile {
643 FILE* m_file;
644 char m_file_name[FN_REFLEN];
645 size_t m_bytes_written;
646public:
647 LogFile() : m_file(NULL), m_bytes_written(0) {
648 bzero(m_file_name, sizeof(m_file_name));
649 }
650
651 ~LogFile() {
652 close();
653 }
654
655 const char* file_name() const { return m_file_name; }
656 size_t bytes_written() const { return m_bytes_written; }
657
658 void open(const char* dir, const char* name, const char* ext)
659 {
660 DBUG_ENTER("LogFile::open");
661 DBUG_PRINT("enter", ("dir: '%s', name: '%s'", dir, name));
662 if (!name)
663 {
664 m_file= stdout;
665 DBUG_VOID_RETURN;
666 }
667
668 fn_format(m_file_name, name, dir, ext,
669 *dir ? MY_REPLACE_DIR | MY_REPLACE_EXT :
670 MY_REPLACE_EXT);
671
672 DBUG_PRINT("info", ("file_name: %s", m_file_name));
673
674 if ((m_file= fopen(m_file_name, "wb+")) == NULL)
675 die("Failed to open log file %s, errno: %d", m_file_name, errno);
676
677 DBUG_VOID_RETURN;
678 }
679
680 void close()
681 {
682 if (m_file) {
683 if (m_file != stdout)
684 fclose(m_file);
685 else
686 fflush(m_file);
687 }
688 m_file= NULL;
689 }
690
691 void flush()
692 {
693 if (m_file && m_file != stdout)
694 {
695 if (fflush(m_file))
696 die("Failed to flush '%s', errno: %d", m_file_name, errno);
697 }
698 }
699
700 void write(DYNAMIC_STRING* ds)
701 {
702 DBUG_ENTER("LogFile::write");
703 DBUG_PRINT("enter", ("length: %u", (uint) ds->length));
704
705 DBUG_ASSERT(m_file);
706
707 if (ds->length == 0)
708 DBUG_VOID_RETURN;
709 DBUG_ASSERT(ds->str);
710
711#ifdef EXTRA_DEBUG
712 DBUG_PRINT("extra", ("str: %*s", (int) ds->length, ds->str));
713#endif
714
715 if (fwrite(ds->str, 1, ds->length, m_file) != ds->length)
716 die("Failed to write %lu bytes to '%s', errno: %d",
717 (unsigned long)ds->length, m_file_name, errno);
718 m_bytes_written+= ds->length;
719 DBUG_VOID_RETURN;
720 }
721
722 void show_tail(uint lines) {
723 DBUG_ENTER("LogFile::show_tail");
724
725 if (!m_file || m_file == stdout)
726 DBUG_VOID_RETURN;
727
728 if (lines == 0)
729 DBUG_VOID_RETURN;
730 lines++;
731
732 int show_offset= 0;
733 char buf[256+1]; /* + zero termination for DBUG_PRINT */
734 size_t bytes;
735 bool found_bof= false;
736
737 /* Search backward in file until "lines" newline has been found */
738 while (lines && !found_bof)
739 {
740 show_offset-= sizeof(buf)-1;
741 while(fseek(m_file, show_offset, SEEK_END) != 0 && show_offset < 0)
742 {
743 found_bof= true;
744 // Seeking before start of file
745 show_offset++;
746 }
747
748 if ((bytes= fread(buf, 1, sizeof(buf)-1, m_file)) <= 0)
749 {
750 // ferror=0 will happen here if no queries executed yet
751 if (ferror(m_file))
752 fprintf(stderr,
753 "Failed to read from '%s', errno: %d, feof:%d, ferror:%d\n",
754 m_file_name, errno, feof(m_file), ferror(m_file));
755 DBUG_VOID_RETURN;
756 }
757
758 DBUG_PRINT("info", ("Read %zu bytes from file, buf: %.*s",
759 bytes, (int)bytes, buf));
760
761 char* show_from= buf + bytes;
762 while(show_from > buf && lines > 0 )
763 {
764 show_from--;
765 if (*show_from == '\n')
766 lines--;
767 }
768 if (show_from != buf)
769 {
770 // The last new line was found in this buf, adjust offset
771 show_offset+= (int)(show_from - buf) + 1;
772 DBUG_PRINT("info", ("adjusted offset to %d", show_offset));
773 }
774 DBUG_PRINT("info", ("show_offset: %d", show_offset));
775 }
776
777 fprintf(stderr, "\nThe result from queries just before the failure was:\n");
778
779 DBUG_PRINT("info", ("show_offset: %d", show_offset));
780 if (!lines)
781 {
782 fprintf(stderr, "< snip >\n");
783
784 if (fseek(m_file, show_offset, SEEK_END) != 0)
785 {
786 fprintf(stderr, "Failed to seek to position %d in '%s', errno: %d",
787 show_offset, m_file_name, errno);
788 DBUG_VOID_RETURN;
789 }
790
791 }
792 else {
793 DBUG_PRINT("info", ("Showing the whole file"));
794 if (fseek(m_file, 0L, SEEK_SET) != 0)
795 {
796 fprintf(stderr, "Failed to seek to pos 0 in '%s', errno: %d",
797 m_file_name, errno);
798 DBUG_VOID_RETURN;
799 }
800 }
801
802 while ((bytes= fread(buf, 1, sizeof(buf)-1, m_file)) > 0)
803 if (bytes != fwrite(buf, 1, bytes, stderr))
804 die("Failed to write to '%s', errno: %d",
805 m_file_name, errno);
806
807 if (!lines)
808 {
809 fprintf(stderr,
810 "\nMore results from queries before failure can be found in %s\n",
811 m_file_name);
812 }
813 fflush(stderr);
814
815 DBUG_VOID_RETURN;
816 }
817};
818
819LogFile log_file;
820LogFile progress_file;
821
822void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, size_t len);
823void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val);
824void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val);
825void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING* ds_input,
826 bool keep_header);
827
828static int match_expected_error(struct st_command *command,
829 unsigned int err_errno,
830 const char *err_sqlstate);
831void handle_error(struct st_command*,
832 unsigned int err_errno, const char *err_error,
833 const char *err_sqlstate, DYNAMIC_STRING *ds);
834void handle_no_error(struct st_command*);
835void revert_properties();
836
837static void handle_no_active_connection(struct st_command* command,
838 struct st_connection *cn, DYNAMIC_STRING *ds);
839
840
841/* Wrapper for fgets.Strips \r off newlines on Windows.
842 Should be used with together with my_popen().
843*/
844static char *my_fgets(char * s, int n, FILE * stream, int *len)
845{
846 char *buf = fgets(s, n, stream);
847 if (!buf)
848 {
849 *len= 0;
850 return buf;
851 }
852
853 *len = (int)strlen(buf);
854#ifdef _WIN32
855 /* Strip '\r' off newlines. */
856 if (*len > 1 && buf[*len - 2] == '\r' && buf[*len - 1] == '\n')
857 {
858 buf[*len - 2]= '\n';
859 buf[*len - 1]= 0;
860 (*len)--;
861 }
862#endif
863 return buf;
864}
865
866/*
867 Wrapper for popen().
868 On Windows, uses binary mode to workaround
869 C runtime bug mentioned in MDEV-9409
870*/
871static FILE* my_popen(const char *cmd, const char *mode)
872{
873 FILE *f= popen(cmd, mode);
874#ifdef _WIN32
875 if (f)
876 _setmode(fileno(f), O_BINARY);
877#endif
878 return f;
879}
880
881#ifdef EMBEDDED_LIBRARY
882
883#define EMB_SEND_QUERY 1
884#define EMB_READ_QUERY_RESULT 2
885#define EMB_END_CONNECTION 3
886#define EMB_PREPARE_STMT 4
887#define EMB_EXECUTE_STMT 5
888#define EMB_CLOSE_STMT 6
889
890/* workaround for MySQL BUG#57491 */
891#undef MY_WME
892#define MY_WME 0
893
894/* attributes of the query thread */
895pthread_attr_t cn_thd_attrib;
896
897
898/*
899 This procedure represents the connection and actually
900 runs queries when in the EMBEDDED-SERVER mode.
901 The run_query_normal() just sends request for running
902 mysql_send_query and mysql_read_query_result() here.
903*/
904
905pthread_handler_t connection_thread(void *arg)
906{
907 struct st_connection *cn= (struct st_connection*)arg;
908
909 mysql_thread_init();
910 while (cn->command != EMB_END_CONNECTION)
911 {
912 if (!cn->command)
913 {
914 pthread_mutex_lock(&cn->query_mutex);
915 while (!cn->command)
916 pthread_cond_wait(&cn->query_cond, &cn->query_mutex);
917 pthread_mutex_unlock(&cn->query_mutex);
918 }
919 switch (cn->command)
920 {
921 case EMB_END_CONNECTION:
922 goto end_thread;
923 case EMB_SEND_QUERY:
924 cn->result= mysql_send_query(cn->mysql,
925 cn->cur_query, cn->cur_query_len);
926 break;
927 case EMB_READ_QUERY_RESULT:
928 cn->result= mysql_read_query_result(cn->mysql);
929 break;
930 case EMB_PREPARE_STMT:
931 cn->result= mysql_stmt_prepare(cn->stmt,
932 cn->cur_query, cn->cur_query_len);
933 break;
934 case EMB_EXECUTE_STMT:
935 cn->result= mysql_stmt_execute(cn->stmt);
936 break;
937 case EMB_CLOSE_STMT:
938 cn->result= mysql_stmt_close(cn->stmt);
939 break;
940 default:
941 DBUG_ASSERT(0);
942 }
943 cn->command= 0;
944 pthread_mutex_lock(&cn->result_mutex);
945 cn->query_done= 1;
946 pthread_cond_signal(&cn->result_cond);
947 pthread_mutex_unlock(&cn->result_mutex);
948 }
949
950end_thread:
951 cn->query_done= 1;
952 mysql_thread_end();
953 pthread_exit(0);
954 return 0;
955}
956
957static void wait_query_thread_done(struct st_connection *con)
958{
959 DBUG_ASSERT(con->has_thread);
960 if (!con->query_done)
961 {
962 pthread_mutex_lock(&con->result_mutex);
963 while (!con->query_done)
964 pthread_cond_wait(&con->result_cond, &con->result_mutex);
965 pthread_mutex_unlock(&con->result_mutex);
966 }
967}
968
969
970static void signal_connection_thd(struct st_connection *cn, int command)
971{
972 DBUG_ASSERT(cn->has_thread);
973 cn->query_done= 0;
974 cn->command= command;
975 pthread_mutex_lock(&cn->query_mutex);
976 pthread_cond_signal(&cn->query_cond);
977 pthread_mutex_unlock(&cn->query_mutex);
978}
979
980
981/*
982 Sometimes we try to execute queries when the connection is closed.
983 It's done to make sure it was closed completely.
984 So that if our connection is closed (cn->has_thread == 0), we just return
985 the mysql_send_query() result which is an error in this case.
986*/
987
988static int do_send_query(struct st_connection *cn, const char *q, int q_len)
989{
990 if (!cn->has_thread)
991 return mysql_send_query(cn->mysql, q, q_len);
992 cn->cur_query= q;
993 cn->cur_query_len= q_len;
994 signal_connection_thd(cn, EMB_SEND_QUERY);
995 return 0;
996}
997
998static int do_read_query_result(struct st_connection *cn)
999{
1000 DBUG_ASSERT(cn->has_thread);
1001 wait_query_thread_done(cn);
1002 if (cn->result)
1003 goto exit_func;
1004
1005 signal_connection_thd(cn, EMB_READ_QUERY_RESULT);
1006 wait_query_thread_done(cn);
1007
1008exit_func:
1009 return cn->result;
1010}
1011
1012
1013static int do_stmt_prepare(struct st_connection *cn, const char *q, int q_len)
1014{
1015 /* The cn->stmt is already set. */
1016 if (!cn->has_thread)
1017 return mysql_stmt_prepare(cn->stmt, q, q_len);
1018 cn->cur_query= q;
1019 cn->cur_query_len= q_len;
1020 signal_connection_thd(cn, EMB_PREPARE_STMT);
1021 wait_query_thread_done(cn);
1022 return cn->result;
1023}
1024
1025
1026static int do_stmt_execute(struct st_connection *cn)
1027{
1028 /* The cn->stmt is already set. */
1029 if (!cn->has_thread)
1030 return mysql_stmt_execute(cn->stmt);
1031 signal_connection_thd(cn, EMB_EXECUTE_STMT);
1032 wait_query_thread_done(cn);
1033 return cn->result;
1034}
1035
1036
1037static int do_stmt_close(struct st_connection *cn)
1038{
1039 /* The cn->stmt is already set. */
1040 if (!cn->has_thread)
1041 return mysql_stmt_close(cn->stmt);
1042 signal_connection_thd(cn, EMB_CLOSE_STMT);
1043 wait_query_thread_done(cn);
1044 return cn->result;
1045}
1046
1047
1048static void emb_close_connection(struct st_connection *cn)
1049{
1050 if (!cn->has_thread)
1051 return;
1052 wait_query_thread_done(cn);
1053 signal_connection_thd(cn, EMB_END_CONNECTION);
1054 pthread_join(cn->tid, NULL);
1055 cn->has_thread= FALSE;
1056 pthread_mutex_destroy(&cn->query_mutex);
1057 pthread_cond_destroy(&cn->query_cond);
1058 pthread_mutex_destroy(&cn->result_mutex);
1059 pthread_cond_destroy(&cn->result_cond);
1060}
1061
1062
1063static void init_connection_thd(struct st_connection *cn)
1064{
1065 cn->query_done= 1;
1066 cn->command= 0;
1067 if (pthread_mutex_init(&cn->query_mutex, NULL) ||
1068 pthread_cond_init(&cn->query_cond, NULL) ||
1069 pthread_mutex_init(&cn->result_mutex, NULL) ||
1070 pthread_cond_init(&cn->result_cond, NULL) ||
1071 pthread_create(&cn->tid, &cn_thd_attrib, connection_thread, (void*)cn))
1072 die("Error in the thread library");
1073 cn->has_thread=TRUE;
1074}
1075
1076#else /* ! EMBEDDED_LIBRARY*/
1077
1078#define init_connection_thd(X) do { } while(0)
1079#define do_send_query(cn,q,q_len) mysql_send_query(cn->mysql, q, (ulong)q_len)
1080#define do_read_query_result(cn) mysql_read_query_result(cn->mysql)
1081#define do_stmt_prepare(cn, q, q_len) mysql_stmt_prepare(cn->stmt, q, (ulong)q_len)
1082#define do_stmt_execute(cn) mysql_stmt_execute(cn->stmt)
1083#define do_stmt_close(cn) mysql_stmt_close(cn->stmt)
1084
1085#endif /*EMBEDDED_LIBRARY*/
1086
1087void do_eval(DYNAMIC_STRING *query_eval, const char *query,
1088 const char *query_end, my_bool pass_through_escape_chars)
1089{
1090 const char *p;
1091 char c, next_c;
1092 int escaped = 0;
1093 VAR *v;
1094 DBUG_ENTER("do_eval");
1095
1096 for (p= query; (c= *p) && p < query_end; ++p)
1097 {
1098 switch(c) {
1099 case '$':
1100 if (escaped)
1101 {
1102 escaped= 0;
1103 dynstr_append_mem(query_eval, p, 1);
1104 }
1105 else
1106 {
1107 if (!(v= var_get(p, &p, 0, 0)))
1108 {
1109 report_or_die( "Bad variable in eval");
1110 DBUG_VOID_RETURN;
1111 }
1112 dynstr_append_mem(query_eval, v->str_val, v->str_val_len);
1113 }
1114 break;
1115 case '\\':
1116 next_c= *(p+1);
1117 if (escaped)
1118 {
1119 escaped= 0;
1120 dynstr_append_mem(query_eval, p, 1);
1121 }
1122 else if (next_c == '\\' || next_c == '$' || next_c == '"')
1123 {
1124 /* Set escaped only if next char is \, " or $ */
1125 escaped= 1;
1126
1127 if (pass_through_escape_chars)
1128 {
1129 /* The escape char should be added to the output string. */
1130 dynstr_append_mem(query_eval, p, 1);
1131 }
1132 }
1133 else
1134 dynstr_append_mem(query_eval, p, 1);
1135 break;
1136 default:
1137 escaped= 0;
1138 dynstr_append_mem(query_eval, p, 1);
1139 break;
1140 }
1141 }
1142 fix_win_paths(query_eval->str, query_eval->length);
1143 DBUG_VOID_RETURN;
1144}
1145
1146
1147/*
1148 Run query and dump the result to stderr in vertical format
1149
1150 NOTE! This function should be safe to call when an error
1151 has occurred and thus any further errors will be ignored (although logged)
1152
1153 SYNOPSIS
1154 show_query
1155 mysql - connection to use
1156 query - query to run
1157
1158*/
1159
1160static void show_query(MYSQL* mysql, const char* query)
1161{
1162 MYSQL_RES* res;
1163 DBUG_ENTER("show_query");
1164
1165 if (!mysql)
1166 DBUG_VOID_RETURN;
1167
1168 if (mysql_query(mysql, query))
1169 {
1170 log_msg("Error running query '%s': %d %s",
1171 query, mysql_errno(mysql), mysql_error(mysql));
1172 DBUG_VOID_RETURN;
1173 }
1174
1175 if ((res= mysql_store_result(mysql)) == NULL)
1176 {
1177 /* No result set returned */
1178 DBUG_VOID_RETURN;
1179 }
1180
1181 {
1182 MYSQL_ROW row;
1183 unsigned int i;
1184 unsigned int row_num= 0;
1185 unsigned int num_fields= mysql_num_fields(res);
1186 MYSQL_FIELD *fields= mysql_fetch_fields(res);
1187
1188 fprintf(stderr, "=== %s ===\n", query);
1189 while ((row= mysql_fetch_row(res)))
1190 {
1191 unsigned long *lengths= mysql_fetch_lengths(res);
1192 row_num++;
1193
1194 fprintf(stderr, "---- %d. ----\n", row_num);
1195 for(i= 0; i < num_fields; i++)
1196 {
1197 fprintf(stderr, "%s\t%.*s\n",
1198 fields[i].name,
1199 (int)lengths[i], row[i] ? row[i] : "NULL");
1200 }
1201 }
1202 for (i= 0; i < strlen(query)+8; i++)
1203 fprintf(stderr, "=");
1204 fprintf(stderr, "\n\n");
1205 }
1206 mysql_free_result(res);
1207
1208 DBUG_VOID_RETURN;
1209}
1210
1211
1212/*
1213 Show any warnings just before the error. Since the last error
1214 is added to the warning stack, only print @@warning_count-1 warnings.
1215
1216 NOTE! This function should be safe to call when an error
1217 has occurred and this any further errors will be ignored(although logged)
1218
1219 SYNOPSIS
1220 show_warnings_before_error
1221 mysql - connection to use
1222
1223*/
1224
1225static void show_warnings_before_error(MYSQL* mysql)
1226{
1227 MYSQL_RES* res;
1228 const char* query= "SHOW WARNINGS";
1229 DBUG_ENTER("show_warnings_before_error");
1230
1231 if (!mysql)
1232 DBUG_VOID_RETURN;
1233
1234 if (mysql_query(mysql, query))
1235 {
1236 log_msg("Error running query '%s': %d %s",
1237 query, mysql_errno(mysql), mysql_error(mysql));
1238 DBUG_VOID_RETURN;
1239 }
1240
1241 if ((res= mysql_store_result(mysql)) == NULL)
1242 {
1243 /* No result set returned */
1244 DBUG_VOID_RETURN;
1245 }
1246
1247 if (mysql_num_rows(res) <= 1)
1248 {
1249 /* Don't display the last row, it's "last error" */
1250 }
1251 else
1252 {
1253 MYSQL_ROW row;
1254 unsigned int row_num= 0;
1255 unsigned int num_fields= mysql_num_fields(res);
1256
1257 fprintf(stderr, "\nWarnings from just before the error:\n");
1258 while ((row= mysql_fetch_row(res)))
1259 {
1260 unsigned int i;
1261 unsigned long *lengths= mysql_fetch_lengths(res);
1262
1263 if (++row_num >= mysql_num_rows(res))
1264 {
1265 /* Don't display the last row, it's "last error" */
1266 break;
1267 }
1268
1269 for(i= 0; i < num_fields; i++)
1270 {
1271 fprintf(stderr, "%.*s ", (int)lengths[i],
1272 row[i] ? row[i] : "NULL");
1273 }
1274 fprintf(stderr, "\n");
1275 }
1276 }
1277 mysql_free_result(res);
1278
1279 DBUG_VOID_RETURN;
1280}
1281
1282
1283enum arg_type
1284{
1285 ARG_STRING,
1286 ARG_REST
1287};
1288
1289struct command_arg {
1290 const char *argname; /* Name of argument */
1291 enum arg_type type; /* Type of argument */
1292 my_bool required; /* Argument required */
1293 DYNAMIC_STRING *ds; /* Storage for argument */
1294 const char *description; /* Description of the argument */
1295};
1296
1297
1298void check_command_args(struct st_command *command,
1299 const char *arguments,
1300 const struct command_arg *args,
1301 int num_args, const char delimiter_arg)
1302{
1303 int i;
1304 const char *ptr= arguments;
1305 const char *start;
1306 DBUG_ENTER("check_command_args");
1307 DBUG_PRINT("enter", ("num_args: %d", num_args));
1308
1309 for (i= 0; i < num_args; i++)
1310 {
1311 const struct command_arg *arg= &args[i];
1312 char delimiter;
1313
1314 switch (arg->type) {
1315 /* A string */
1316 case ARG_STRING:
1317 /* Skip leading spaces */
1318 while (*ptr && *ptr == ' ')
1319 ptr++;
1320 start= ptr;
1321 delimiter = delimiter_arg;
1322 /* If start of arg is ' ` or " search to matching quote end instead */
1323 if (*ptr && strchr ("'`\"", *ptr))
1324 {
1325 delimiter= *ptr;
1326 start= ++ptr;
1327 }
1328 /* Find end of arg, terminated by "delimiter" */
1329 while (*ptr && *ptr != delimiter)
1330 ptr++;
1331 if (ptr > start)
1332 {
1333 init_dynamic_string(arg->ds, 0, ptr-start, 32);
1334 do_eval(arg->ds, start, ptr, FALSE);
1335 }
1336 else
1337 {
1338 /* Empty string */
1339 init_dynamic_string(arg->ds, "", 0, 0);
1340 }
1341 /* Find real end of arg, terminated by "delimiter_arg" */
1342 /* This will do nothing if arg was not closed by quotes */
1343 while (*ptr && *ptr != delimiter_arg)
1344 ptr++;
1345
1346 command->last_argument= (char*)ptr;
1347
1348 /* Step past the delimiter */
1349 if (*ptr && *ptr == delimiter_arg)
1350 ptr++;
1351 DBUG_PRINT("info", ("val: %s", arg->ds->str));
1352 break;
1353
1354 /* Rest of line */
1355 case ARG_REST:
1356 start= ptr;
1357 init_dynamic_string(arg->ds, 0, command->query_len, 256);
1358 do_eval(arg->ds, start, command->end, FALSE);
1359 command->last_argument= command->end;
1360 DBUG_PRINT("info", ("val: %s", arg->ds->str));
1361 break;
1362
1363 default:
1364 DBUG_ASSERT("Unknown argument type");
1365 break;
1366 }
1367
1368 /* Check required arg */
1369 if (arg->ds->length == 0 && arg->required)
1370 die("Missing required argument '%s' to command '%.*s'", arg->argname,
1371 command->first_word_len, command->query);
1372
1373 }
1374 /* Check for too many arguments passed */
1375 ptr= command->last_argument;
1376 while(ptr <= command->end && *ptr != '#')
1377 {
1378 if (*ptr && *ptr != ' ')
1379 die("Extra argument '%s' passed to '%.*s'",
1380 ptr, command->first_word_len, command->query);
1381 ptr++;
1382 }
1383 DBUG_VOID_RETURN;
1384}
1385
1386void handle_command_error(struct st_command *command, uint error,
1387 int sys_errno)
1388{
1389 DBUG_ENTER("handle_command_error");
1390 DBUG_PRINT("enter", ("error: %d", error));
1391 var_set_int("$sys_errno",sys_errno);
1392 var_set_int("$errno",error);
1393 if (error != 0)
1394 {
1395 int i;
1396
1397 if (command->abort_on_error)
1398 {
1399 report_or_die("command \"%.*s\" failed with error: %u my_errno: %d "
1400 "errno: %d",
1401 command->first_word_len, command->query, error, my_errno,
1402 sys_errno);
1403 DBUG_VOID_RETURN;
1404 }
1405
1406 i= match_expected_error(command, error, NULL);
1407
1408 if (i >= 0)
1409 {
1410 DBUG_PRINT("info", ("command \"%.*s\" failed with expected error: %u, errno: %d",
1411 command->first_word_len, command->query, error,
1412 sys_errno));
1413 revert_properties();
1414 DBUG_VOID_RETURN;
1415 }
1416 if (command->expected_errors.count > 0)
1417 report_or_die("command \"%.*s\" failed with wrong error: %u "
1418 "my_errno: %d errno: %d",
1419 command->first_word_len, command->query, error, my_errno,
1420 sys_errno);
1421 }
1422 else if (command->expected_errors.err[0].type == ERR_ERRNO &&
1423 command->expected_errors.err[0].code.errnum != 0)
1424 {
1425 /* Error code we wanted was != 0, i.e. not an expected success */
1426 report_or_die("command \"%.*s\" succeeded - should have failed with "
1427 "errno %d...",
1428 command->first_word_len, command->query,
1429 command->expected_errors.err[0].code.errnum);
1430 }
1431 revert_properties();
1432 DBUG_VOID_RETURN;
1433}
1434
1435
1436void close_connections()
1437{
1438 DBUG_ENTER("close_connections");
1439 for (--next_con; next_con >= connections; --next_con)
1440 {
1441 if (next_con->stmt)
1442 do_stmt_close(next_con);
1443#ifdef EMBEDDED_LIBRARY
1444 emb_close_connection(next_con);
1445#endif
1446 next_con->stmt= 0;
1447 mysql_close(next_con->mysql);
1448 next_con->mysql= 0;
1449 if (next_con->util_mysql)
1450 mysql_close(next_con->util_mysql);
1451 my_free(next_con->name);
1452 }
1453 my_free(connections);
1454 DBUG_VOID_RETURN;
1455}
1456
1457
1458void close_statements()
1459{
1460 struct st_connection *con;
1461 DBUG_ENTER("close_statements");
1462 for (con= connections; con < next_con; con++)
1463 {
1464 if (con->stmt)
1465 do_stmt_close(con);
1466 con->stmt= 0;
1467 }
1468 DBUG_VOID_RETURN;
1469}
1470
1471
1472void close_files()
1473{
1474 DBUG_ENTER("close_files");
1475 for (; cur_file >= file_stack; cur_file--)
1476 {
1477 if (cur_file->file && cur_file->file != stdin)
1478 {
1479 DBUG_PRINT("info", ("closing file: %s", cur_file->file_name));
1480 fclose(cur_file->file);
1481 }
1482 my_free(cur_file->file_name);
1483 cur_file->file_name= 0;
1484 }
1485 DBUG_VOID_RETURN;
1486}
1487
1488
1489void free_used_memory()
1490{
1491 uint i;
1492 DBUG_ENTER("free_used_memory");
1493
1494 if (connections)
1495 close_connections();
1496 close_files();
1497 my_hash_free(&var_hash);
1498
1499 for (i= 0 ; i < q_lines.elements ; i++)
1500 {
1501 struct st_command **q= dynamic_element(&q_lines, i, struct st_command**);
1502 my_free((*q)->query_buf);
1503 if ((*q)->eval_query.str)
1504 dynstr_free(&(*q)->eval_query);
1505 if ((*q)->content.str)
1506 dynstr_free(&(*q)->content);
1507 my_free((*q));
1508 }
1509 for (i= 0; i < 10; i++)
1510 {
1511 if (var_reg[i].alloced_len)
1512 my_free(var_reg[i].str_val);
1513 }
1514 while (embedded_server_arg_count > 1)
1515 my_free(embedded_server_args[--embedded_server_arg_count]);
1516 delete_dynamic(&q_lines);
1517 dynstr_free(&ds_res);
1518 if (ds_warn)
1519 dynstr_free(ds_warn);
1520 free_all_replace();
1521 my_free(opt_pass);
1522 free_defaults(default_argv);
1523 free_root(&require_file_root, MYF(0));
1524 free_re();
1525#ifdef _WIN32
1526 free_tmp_sh_file();
1527 free_win_path_patterns();
1528#endif
1529 DBUG_VOID_RETURN;
1530}
1531
1532
1533static void cleanup_and_exit(int exit_code)
1534{
1535 free_used_memory();
1536
1537 /* Only call mysql_server_end if mysql_server_init has been called */
1538 if (server_initialized)
1539 mysql_server_end();
1540
1541 /*
1542 mysqltest is fundamentally written in a way that makes impossible
1543 to free all memory before exit (consider memory allocated
1544 for frame local DYNAMIC_STRING's and die() invoked down the stack.
1545
1546 We close stderr here to stop unavoidable safemalloc reports
1547 from polluting the output.
1548 */
1549 fclose(stderr);
1550
1551 my_end(my_end_arg);
1552
1553 if (!silent) {
1554 switch (exit_code) {
1555 case 1:
1556 printf("not ok\n");
1557 break;
1558 case 0:
1559 printf("ok\n");
1560 break;
1561 case 62:
1562 printf("skipped\n");
1563 break;
1564 default:
1565 printf("unknown exit code: %d\n", exit_code);
1566 DBUG_ASSERT(0);
1567 }
1568 }
1569
1570 exit(exit_code);
1571}
1572
1573size_t print_file_stack(char *s, const char *end)
1574{
1575 char *start= s;
1576 struct st_test_file* err_file= cur_file;
1577 if (err_file == file_stack)
1578 return 0;
1579
1580 for (;;)
1581 {
1582 err_file--;
1583 s+= my_snprintf(s, end - s, "included from %s at line %d:\n",
1584 err_file->file_name, err_file->lineno);
1585 if (err_file == file_stack)
1586 break;
1587 }
1588 return s - start;
1589}
1590
1591
1592static void make_error_message(char *buf, size_t len, const char *fmt, va_list args)
1593{
1594 char *s= buf, *end= buf + len;
1595 s+= my_snprintf(s, end - s, "mysqltest: ");
1596 if (cur_file && cur_file != file_stack)
1597 {
1598 s+= my_snprintf(s, end - s, "In included file \"%s\": \n",
1599 cur_file->file_name);
1600 s+= print_file_stack(s, end);
1601 }
1602
1603 if (start_lineno > 0)
1604 s+= my_snprintf(s, end -s, "At line %u: ", start_lineno);
1605 if (!fmt)
1606 fmt= "unknown error";
1607
1608 s+= my_vsnprintf(s, end - s, fmt, args);
1609 s+= my_snprintf(s, end -s, "\n");
1610}
1611
1612void die(const char *fmt, ...)
1613{
1614 char buff[DIE_BUFF_SIZE];
1615 va_list args;
1616 va_start(args, fmt);
1617 make_error_message(buff, sizeof(buff), fmt, args);
1618 really_die(buff);
1619}
1620
1621void really_die(const char *msg)
1622{
1623 static int dying= 0;
1624 fflush(stdout);
1625 fprintf(stderr, "%s", msg);
1626 fflush(stderr);
1627
1628 /*
1629 Protect against dying twice
1630 first time 'die' is called, try to write log files
1631 second time, just exit
1632 */
1633 if (dying)
1634 cleanup_and_exit(1);
1635 dying= 1;
1636
1637 log_file.show_tail(opt_tail_lines);
1638
1639 /*
1640 Help debugging by displaying any warnings that might have
1641 been produced prior to the error
1642 */
1643 if (cur_con && !cur_con->pending)
1644 show_warnings_before_error(cur_con->mysql);
1645
1646 cleanup_and_exit(1);
1647}
1648
1649void report_or_die(const char *fmt, ...)
1650{
1651 va_list args;
1652 DBUG_ENTER("report_or_die");
1653
1654 char buff[DIE_BUFF_SIZE];
1655
1656 va_start(args, fmt);
1657 make_error_message(buff, sizeof(buff), fmt, args);
1658 va_end(args);
1659
1660 if (opt_continue_on_error)
1661 {
1662 /* Just log the error and continue */
1663 replace_dynstr_append(&ds_res, buff);
1664 error_count++;
1665 DBUG_VOID_RETURN;
1666 }
1667
1668 really_die(buff);
1669}
1670
1671
1672void abort_not_supported_test(const char *fmt, ...)
1673{
1674 va_list args;
1675 DBUG_ENTER("abort_not_supported_test");
1676
1677 /* Print include filestack */
1678 fflush(stdout);
1679 fprintf(stderr, "The test '%s' is not supported by this installation\n",
1680 file_stack->file_name);
1681 fprintf(stderr, "Detected in file %s at line %d\n",
1682 cur_file->file_name, cur_file->lineno);
1683
1684 char buff[DIE_BUFF_SIZE];
1685 print_file_stack(buff, buff + sizeof(buff));
1686 fprintf(stderr, "%s", buff);
1687
1688 /* Print error message */
1689 va_start(args, fmt);
1690 if (fmt)
1691 {
1692 fprintf(stderr, "reason: ");
1693 vfprintf(stderr, fmt, args);
1694 fprintf(stderr, "\n");
1695 fflush(stderr);
1696 }
1697 va_end(args);
1698
1699 cleanup_and_exit(62);
1700}
1701
1702
1703void abort_not_in_this_version()
1704{
1705 die("Not available in this version of mysqltest");
1706}
1707
1708
1709void verbose_msg(const char *fmt, ...)
1710{
1711 va_list args;
1712 DBUG_ENTER("verbose_msg");
1713 DBUG_PRINT("enter", ("format: %s", fmt));
1714
1715 if (!verbose)
1716 DBUG_VOID_RETURN;
1717
1718 fflush(stdout);
1719 va_start(args, fmt);
1720 fprintf(stderr, "mysqltest: ");
1721 if (cur_file && cur_file != file_stack)
1722 fprintf(stderr, "In included file \"%s\": ",
1723 cur_file->file_name);
1724 if (start_lineno != 0)
1725 fprintf(stderr, "At line %u: ", start_lineno);
1726 vfprintf(stderr, fmt, args);
1727 fprintf(stderr, "\n");
1728 va_end(args);
1729 fflush(stderr);
1730
1731 DBUG_VOID_RETURN;
1732}
1733
1734
1735void log_msg(const char *fmt, ...)
1736{
1737 va_list args;
1738 char buff[1024];
1739 size_t len;
1740 DBUG_ENTER("log_msg");
1741
1742 va_start(args, fmt);
1743 len= my_vsnprintf(buff, sizeof(buff)-1, fmt, args);
1744 va_end(args);
1745
1746 dynstr_append_mem(&ds_res, buff, len);
1747 dynstr_append(&ds_res, "\n");
1748
1749 DBUG_VOID_RETURN;
1750}
1751
1752
1753/*
1754 Read a file and append it to ds
1755
1756 SYNOPSIS
1757 cat_file
1758 ds - pointer to dynamic string where to add the files content
1759 filename - name of the file to read
1760
1761*/
1762
1763int cat_file(DYNAMIC_STRING* ds, const char* filename)
1764{
1765 int fd;
1766 size_t len;
1767 char *buff;
1768
1769 if ((fd= my_open(filename, O_RDONLY, MYF(0))) < 0)
1770 return 1;
1771
1772 len= (size_t) my_seek(fd, 0, SEEK_END, MYF(0));
1773 my_seek(fd, 0, SEEK_SET, MYF(0));
1774 if (len == (size_t)MY_FILEPOS_ERROR ||
1775 !(buff= (char*)my_malloc(len + 1, MYF(0))))
1776 {
1777 my_close(fd, MYF(0));
1778 return 1;
1779 }
1780 len= my_read(fd, (uchar*)buff, len, MYF(0));
1781 my_close(fd, MYF(0));
1782
1783 {
1784 char *p= buff, *start= buff,*end=buff+len;
1785 while (p < end)
1786 {
1787 /* Convert cr/lf to lf */
1788 if (*p == '\r' && p+1 < end && *(p+1)== '\n')
1789 {
1790 /* Add fake newline instead of cr and output the line */
1791 *p= '\n';
1792 p++; /* Step past the "fake" newline */
1793 *p= 0;
1794 replace_dynstr_append_mem(ds, start, p-start);
1795 p++; /* Step past the "fake" newline */
1796 start= p;
1797 }
1798 else
1799 p++;
1800 }
1801 /* Output any chars that migh be left */
1802 *p= 0;
1803 replace_dynstr_append_mem(ds, start, p-start);
1804 }
1805 my_free(buff);
1806 return 0;
1807}
1808
1809
1810/*
1811 Run the specified command with popen
1812
1813 SYNOPSIS
1814 run_command
1815 cmd - command to execute(should be properly quoted
1816 ds_res- pointer to dynamic string where to store the result
1817
1818*/
1819
1820static int run_command(char* cmd,
1821 DYNAMIC_STRING *ds_res)
1822{
1823 char buf[512]= {0};
1824 FILE *res_file;
1825 int error;
1826 DBUG_ENTER("run_command");
1827 DBUG_PRINT("enter", ("cmd: %s", cmd));
1828
1829 if (!(res_file= my_popen(cmd, "r")))
1830 {
1831 report_or_die("popen(\"%s\", \"r\") failed", cmd);
1832 DBUG_RETURN(-1);
1833 }
1834
1835 int len;
1836 while (my_fgets(buf, sizeof(buf), res_file, &len))
1837 {
1838 DBUG_PRINT("info", ("buf: %s", buf));
1839 if(ds_res)
1840 {
1841 /* Save the output of this command in the supplied string */
1842 dynstr_append_mem(ds_res, buf,len);
1843 }
1844 else
1845 {
1846 /* Print it directly on screen */
1847 fprintf(stdout, "%s", buf);
1848 }
1849 }
1850
1851 error= pclose(res_file);
1852 DBUG_RETURN(WEXITSTATUS(error));
1853}
1854
1855
1856/*
1857 Run the specified tool with variable number of arguments
1858
1859 SYNOPSIS
1860 run_tool
1861 tool_path - the name of the tool to run
1862 ds_res - pointer to dynamic string where to store the result
1863 ... - variable number of arguments that will be properly
1864 quoted and appended after the tool's name
1865
1866*/
1867
1868static int run_tool(const char *tool_path, DYNAMIC_STRING *ds_res, ...)
1869{
1870 int ret;
1871 const char* arg;
1872 va_list args;
1873 DYNAMIC_STRING ds_cmdline;
1874
1875 DBUG_ENTER("run_tool");
1876 DBUG_PRINT("enter", ("tool_path: %s", tool_path));
1877
1878 if (init_dynamic_string(&ds_cmdline, IF_WIN("\"", ""), FN_REFLEN, FN_REFLEN))
1879 die("Out of memory");
1880
1881 dynstr_append_os_quoted(&ds_cmdline, tool_path, NullS);
1882 dynstr_append(&ds_cmdline, " ");
1883
1884 va_start(args, ds_res);
1885
1886 while ((arg= va_arg(args, char *)))
1887 {
1888 /* Options should be os quoted */
1889 if (strncmp(arg, "--", 2) == 0)
1890 dynstr_append_os_quoted(&ds_cmdline, arg, NullS);
1891 else
1892 dynstr_append(&ds_cmdline, arg);
1893 dynstr_append(&ds_cmdline, " ");
1894 }
1895
1896 va_end(args);
1897
1898#ifdef _WIN32
1899 dynstr_append(&ds_cmdline, "\"");
1900#endif
1901
1902 DBUG_PRINT("info", ("Running: %s", ds_cmdline.str));
1903 ret= run_command(ds_cmdline.str, ds_res);
1904 DBUG_PRINT("exit", ("ret: %d", ret));
1905 dynstr_free(&ds_cmdline);
1906 DBUG_RETURN(ret);
1907}
1908
1909
1910/*
1911 Test if diff is present. This is needed on Windows systems
1912 as the OS returns 1 whether diff is successful or if it is
1913 not present.
1914
1915 We run diff -v and look for output in stdout.
1916 We don't redirect stderr to stdout to make for a simplified check
1917 Windows will output '"diff"' is not recognized... to stderr if it is
1918 not present.
1919*/
1920
1921#ifdef _WIN32
1922
1923static int diff_check(const char *diff_name)
1924{
1925 FILE *res_file;
1926 char buf[128];
1927 int have_diff= 0;
1928
1929 my_snprintf(buf, sizeof(buf), "%s -v", diff_name);
1930
1931 if (!(res_file= my_popen(buf, "r")))
1932 die("popen(\"%s\", \"r\") failed", buf);
1933
1934 /*
1935 if diff is not present, nothing will be in stdout to increment
1936 have_diff
1937 */
1938 int len;
1939 if (my_fgets(buf, sizeof(buf), res_file, &len))
1940 have_diff= 1;
1941
1942 pclose(res_file);
1943
1944 return have_diff;
1945}
1946
1947#endif
1948
1949
1950/*
1951 Show the diff of two files using the systems builtin diff
1952 command. If no such diff command exist, just dump the content
1953 of the two files and inform about how to get "diff"
1954
1955 SYNOPSIS
1956 show_diff
1957 ds - pointer to dynamic string where to add the diff(may be NULL)
1958 filename1 - name of first file
1959 filename2 - name of second file
1960
1961*/
1962
1963void show_diff(DYNAMIC_STRING* ds,
1964 const char* filename1, const char* filename2)
1965{
1966 DYNAMIC_STRING ds_tmp;
1967 const char *diff_name = 0;
1968
1969 if (init_dynamic_string(&ds_tmp, "", 256, 256))
1970 die("Out of memory");
1971
1972 /* determine if we have diff on Windows
1973 needs special processing due to return values
1974 on that OS
1975 This test is only done on Windows since it's only needed there
1976 in order to correctly detect non-availibility of 'diff', and
1977 the way it's implemented does not work with default 'diff' on Solaris.
1978 */
1979#ifdef _WIN32
1980 if (diff_check("diff"))
1981 diff_name = "diff";
1982 else if (diff_check("mtrdiff"))
1983 diff_name = "mtrdiff";
1984 else
1985 diff_name = 0;
1986#else
1987 diff_name = "diff"; /* Otherwise always assume it's called diff */
1988#endif
1989
1990 if (diff_name)
1991 {
1992 /* First try with unified diff */
1993 if (run_tool(diff_name,
1994 &ds_tmp, /* Get output from diff in ds_tmp */
1995 "-u",
1996 filename1,
1997 filename2,
1998 "2>&1",
1999 NULL) > 1) /* Most "diff" tools return >1 if error */
2000 {
2001 dynstr_set(&ds_tmp, "");
2002
2003 /* Fallback to context diff with "diff -c" */
2004 if (run_tool(diff_name,
2005 &ds_tmp, /* Get output from diff in ds_tmp */
2006 "-c",
2007 filename1,
2008 filename2,
2009 "2>&1",
2010 NULL) > 1) /* Most "diff" tools return >1 if error */
2011 {
2012 dynstr_set(&ds_tmp, "");
2013
2014 /* Fallback to simple diff with "diff" */
2015 if (run_tool(diff_name,
2016 &ds_tmp, /* Get output from diff in ds_tmp */
2017 filename1,
2018 filename2,
2019 "2>&1",
2020 NULL) > 1) /* Most "diff" tools return >1 if error */
2021 {
2022 diff_name= 0;
2023 }
2024 }
2025 }
2026 }
2027
2028 if (! diff_name)
2029 {
2030 /*
2031 Fallback to dump both files to result file and inform
2032 about installing "diff"
2033 */
2034 dynstr_append(&ds_tmp, "\n");
2035 dynstr_append(&ds_tmp,
2036"\n"
2037"The two files differ but it was not possible to execute 'diff' in\n"
2038"order to show only the difference. Instead the whole content of the\n"
2039"two files was shown for you to diff manually.\n\n"
2040"To get a better report you should install 'diff' on your system, which you\n"
2041"for example can get from http://www.gnu.org/software/diffutils/diffutils.html\n"
2042#ifdef _WIN32
2043"or http://gnuwin32.sourceforge.net/packages/diffutils.htm\n"
2044#endif
2045"\n");
2046
2047 dynstr_append(&ds_tmp, " --- ");
2048 dynstr_append(&ds_tmp, filename1);
2049 dynstr_append(&ds_tmp, " >>>\n");
2050 cat_file(&ds_tmp, filename1);
2051 dynstr_append(&ds_tmp, "<<<\n --- ");
2052 dynstr_append(&ds_tmp, filename1);
2053 dynstr_append(&ds_tmp, " >>>\n");
2054 cat_file(&ds_tmp, filename2);
2055 dynstr_append(&ds_tmp, "<<<<\n");
2056 }
2057
2058 if (ds)
2059 {
2060 /* Add the diff to output */
2061 dynstr_append_mem(ds, ds_tmp.str, ds_tmp.length);
2062 }
2063 else
2064 {
2065 /* Print diff directly to stdout */
2066 fprintf(stderr, "%s\n", ds_tmp.str);
2067 }
2068
2069 dynstr_free(&ds_tmp);
2070
2071}
2072
2073
2074enum compare_files_result_enum {
2075 RESULT_OK= 0,
2076 RESULT_CONTENT_MISMATCH= 1,
2077 RESULT_LENGTH_MISMATCH= 2
2078};
2079
2080/*
2081 Compare two files, given a fd to the first file and
2082 name of the second file
2083
2084 SYNOPSIS
2085 compare_files2
2086 fd - Open file descriptor of the first file
2087 filename2 - Name of second file
2088
2089 RETURN VALUES
2090 According to the values in "compare_files_result_enum"
2091
2092*/
2093
2094int compare_files2(File fd1, const char* filename2)
2095{
2096 int error= RESULT_OK;
2097 File fd2;
2098 size_t fd1_length, fd2_length;
2099 DYNAMIC_STRING fd1_result, fd2_result;
2100
2101 if ((fd2= my_open(filename2, O_RDONLY, MYF(0))) < 0)
2102 {
2103 my_close(fd1, MYF(0));
2104 die("Failed to open second file: '%s'", filename2);
2105 }
2106
2107 fd1_length= (size_t) my_seek(fd1, 0, SEEK_END, MYF(0));
2108 fd2_length= (size_t) my_seek(fd2, 0, SEEK_END, MYF(0));
2109
2110 if (init_dynamic_string(&fd1_result, 0, fd1_length, 0) ||
2111 init_dynamic_string(&fd2_result, 0, fd2_length, 0))
2112 die("Out of memory when allocating data for result");
2113
2114 fd1_result.length= fd1_length;
2115 fd2_result.length= fd2_length;
2116
2117 (void) my_seek(fd1, 0, SEEK_SET, MYF(0));
2118 (void) my_seek(fd2, 0, SEEK_SET, MYF(0));
2119 if (my_read(fd1, (uchar*) fd1_result.str, fd1_length, MYF(MY_WME | MY_NABP)))
2120 die("Error when reading data from result file");
2121 if (my_read(fd2, (uchar*) fd2_result.str, fd2_length, MYF(MY_WME | MY_NABP)))
2122 die("Error when reading data from result file");
2123
2124 if (global_subst &&
2125 (fd1_length != fd2_length ||
2126 memcmp(fd1_result.str, fd2_result.str, fd1_length)))
2127 {
2128 /**
2129 @todo MARIA_HACK
2130 This serves for when a test is run with --default-storage-engine=X
2131 where X is not MyISAM: tests using SHOW CREATE TABLE will always fail
2132 because SHOW CREATE TABLE prints X instead of MyISAM. With
2133 --global-subst=X,MyISAM , such trivial differences are eliminated and
2134 test may be reported as passing.
2135 --global-subst is only a quick way to run a lot of existing tests
2136 with Maria and find bugs; it is not good enough for reaching the main
2137 trees when Maria is merged into them.
2138 --global-subst should be removed.
2139 */
2140 size_t global_subst_from_len= strlen(global_subst_from);
2141 size_t global_subst_to_len= strlen(global_subst_to);
2142 while (replace(&fd1_result,
2143 global_subst_from, global_subst_from_len,
2144 global_subst_to, global_subst_to_len) == 0)
2145 /* do nothing */ ;
2146 /* let's compare again to see if it is ok now */
2147 }
2148
2149 if (fd1_result.length != fd2_result.length)
2150 error= RESULT_LENGTH_MISMATCH;
2151 else if ((memcmp(fd1_result.str, fd2_result.str, fd1_result.length)))
2152 error= RESULT_CONTENT_MISMATCH;
2153
2154 my_close(fd2, MYF(0));
2155 dynstr_free(&fd1_result);
2156 dynstr_free(&fd2_result);
2157
2158 return error;
2159}
2160
2161
2162/*
2163 Compare two files, given their filenames
2164
2165 SYNOPSIS
2166 compare_files
2167 filename1 - Name of first file
2168 filename2 - Name of second file
2169
2170 RETURN VALUES
2171 See 'compare_files2'
2172
2173*/
2174
2175int compare_files(const char* filename1, const char* filename2)
2176{
2177 File fd;
2178 int error;
2179
2180 if ((fd= my_open(filename1, O_RDONLY, MYF(0))) < 0)
2181 die("Failed to open first file: '%s'", filename1);
2182
2183 error= compare_files2(fd, filename2);
2184
2185 my_close(fd, MYF(0));
2186
2187 return error;
2188}
2189
2190
2191/*
2192 Compare content of the string in ds to content of file fname
2193
2194 SYNOPSIS
2195 dyn_string_cmp
2196 ds - Dynamic string containing the string o be compared
2197 fname - Name of file to compare with
2198
2199 RETURN VALUES
2200 See 'compare_files2'
2201*/
2202
2203int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname)
2204{
2205 int error;
2206 File fd;
2207 char temp_file_path[FN_REFLEN];
2208
2209 DBUG_ENTER("dyn_string_cmp");
2210 DBUG_PRINT("enter", ("fname: %s", fname));
2211
2212 if ((fd= create_temp_file(temp_file_path, TMPDIR, "tmp", O_SHARE,
2213 MYF(MY_WME))) < 0)
2214 die("Failed to create temporary file for ds");
2215
2216 /* Write ds to temporary file and set file pos to beginning*/
2217 if (my_write(fd, (uchar *) ds->str, ds->length,
2218 MYF(MY_FNABP | MY_WME)) ||
2219 my_seek(fd, 0, SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR)
2220 {
2221 my_close(fd, MYF(0));
2222 /* Remove the temporary file */
2223 my_delete(temp_file_path, MYF(MY_WME));
2224 die("Failed to write file '%s'", temp_file_path);
2225 }
2226
2227 error= compare_files2(fd, fname);
2228
2229 my_close(fd, MYF(0));
2230 /* Remove the temporary file */
2231 my_delete(temp_file_path, MYF(MY_WME));
2232
2233 DBUG_RETURN(error);
2234}
2235
2236
2237/*
2238 Check the content of log against result file
2239
2240 SYNOPSIS
2241 check_result
2242
2243 RETURN VALUES
2244 error - the function will not return
2245
2246*/
2247
2248void check_result()
2249{
2250 const char *mess= 0;
2251
2252 DBUG_ENTER("check_result");
2253 DBUG_ASSERT(result_file_name);
2254 DBUG_PRINT("enter", ("result_file_name: %s", result_file_name));
2255
2256 switch (compare_files(log_file.file_name(), result_file_name)) {
2257 case RESULT_OK:
2258 if (!error_count)
2259 break; /* ok */
2260 mess= "Got errors while running test";
2261 /* Fallthrough */
2262 case RESULT_LENGTH_MISMATCH:
2263 if (!mess)
2264 mess= "Result length mismatch\n";
2265 /* Fallthrough */
2266 case RESULT_CONTENT_MISMATCH:
2267 {
2268 /*
2269 Result mismatched, dump results to .reject file
2270 and then show the diff
2271 */
2272 char reject_file[FN_REFLEN];
2273 size_t reject_length;
2274
2275 if (!mess)
2276 mess= "Result content mismatch\n";
2277
2278 dirname_part(reject_file, result_file_name, &reject_length);
2279
2280 if (access(reject_file, W_OK) == 0)
2281 {
2282 /* Result file directory is writable, save reject file there */
2283 fn_format(reject_file, result_file_name, "",
2284 ".reject", MY_REPLACE_EXT);
2285 }
2286 else
2287 {
2288 /* Put reject file in opt_logdir */
2289 fn_format(reject_file, result_file_name, opt_logdir,
2290 ".reject", MY_REPLACE_DIR | MY_REPLACE_EXT);
2291 }
2292
2293 if (my_copy(log_file.file_name(), reject_file, MYF(0)) != 0)
2294 die("Failed to copy '%s' to '%s', errno: %d",
2295 log_file.file_name(), reject_file, errno);
2296
2297 show_diff(NULL, result_file_name, reject_file);
2298 die("%s", mess);
2299 break;
2300 }
2301 default: /* impossible */
2302 die("Unknown error code from dyn_string_cmp()");
2303 }
2304
2305 DBUG_VOID_RETURN;
2306}
2307
2308
2309/*
2310 Check the content of ds against a require file
2311 If match fails, abort the test with special error code
2312 indicating that test is not supported
2313
2314 SYNOPSIS
2315 check_require
2316 ds - content to be checked
2317 fname - name of file to check against
2318
2319 RETURN VALUES
2320 error - the function will not return
2321
2322*/
2323
2324void check_require(DYNAMIC_STRING* ds, const char *fname)
2325{
2326 DBUG_ENTER("check_require");
2327
2328 if (dyn_string_cmp(ds, fname))
2329 {
2330 char reason[FN_REFLEN];
2331 fn_format(reason, fname, "", "", MY_REPLACE_EXT | MY_REPLACE_DIR);
2332 abort_not_supported_test("Test requires: '%s'", reason);
2333 }
2334 DBUG_VOID_RETURN;
2335}
2336
2337
2338/*
2339 Remove surrounding chars from string
2340
2341 Return 1 if first character is found but not last
2342*/
2343static int strip_surrounding(char* str, char c1, char c2)
2344{
2345 char* ptr= str;
2346
2347 /* Check if the first non space character is c1 */
2348 while(*ptr && my_isspace(charset_info, *ptr))
2349 ptr++;
2350 if (*ptr == c1)
2351 {
2352 /* Replace it with a space */
2353 *ptr= ' ';
2354
2355 /* Last non space charecter should be c2 */
2356 ptr= strend(str)-1;
2357 while(*ptr && my_isspace(charset_info, *ptr))
2358 ptr--;
2359 if (*ptr == c2)
2360 {
2361 /* Replace it with \0 */
2362 *ptr= 0;
2363 }
2364 else
2365 {
2366 /* Mismatch detected */
2367 return 1;
2368 }
2369 }
2370 return 0;
2371}
2372
2373
2374static void strip_parentheses(struct st_command *command)
2375{
2376 if (strip_surrounding(command->first_argument, '(', ')'))
2377 die("%.*s - argument list started with '%c' must be ended with '%c'",
2378 command->first_word_len, command->query, '(', ')');
2379}
2380
2381
2382C_MODE_START
2383
2384static uchar *get_var_key(const uchar* var, size_t *len,
2385 my_bool __attribute__((unused)) t)
2386{
2387 char* key;
2388 key = ((VAR*)var)->name;
2389 *len = ((VAR*)var)->name_len;
2390 return (uchar*)key;
2391}
2392
2393
2394static void var_free(void *v)
2395{
2396 VAR *var= (VAR*) v;
2397 my_free(var->str_val);
2398 if (var->alloced)
2399 my_free(var);
2400}
2401
2402C_MODE_END
2403
2404void var_check_int(VAR *v)
2405{
2406 char *endptr;
2407 char *str= v->str_val;
2408
2409 /* Initially assume not a number */
2410 v->int_val= 0;
2411 v->is_int= false;
2412 v->int_dirty= false;
2413 if (!str) return;
2414
2415 v->int_val = (int) strtol(str, &endptr, 10);
2416 /* It is an int if strtol consumed something up to end/space/tab */
2417 if (endptr > str && (!*endptr || *endptr == ' ' || *endptr == '\t'))
2418 v->is_int= true;
2419}
2420
2421
2422VAR *var_init(VAR *v, const char *name, size_t name_len, const char *val, size_t val_len)
2423{
2424 size_t val_alloc_len;
2425 VAR *tmp_var;
2426 if (!name_len && name)
2427 name_len = strlen(name);
2428 if (!val_len && val)
2429 val_len = strlen(val) ;
2430 if (!val)
2431 val_len= 0;
2432 val_alloc_len = val_len + 16; /* room to grow */
2433 if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var)
2434 + name_len+2, MYF(MY_WME))))
2435 die("Out of memory");
2436
2437 if (name != NULL)
2438 {
2439 tmp_var->name= reinterpret_cast<char*>(tmp_var) + sizeof(*tmp_var);
2440 memcpy(tmp_var->name, name, name_len);
2441 tmp_var->name[name_len]= 0;
2442 }
2443 else
2444 tmp_var->name= NULL;
2445
2446 tmp_var->alloced = (v == 0);
2447
2448 if (!(tmp_var->str_val = (char*)my_malloc(val_alloc_len+1, MYF(MY_WME))))
2449 die("Out of memory");
2450
2451 if (val)
2452 memcpy(tmp_var->str_val, val, val_len);
2453 tmp_var->str_val[val_len]= 0;
2454
2455 var_check_int(tmp_var);
2456 tmp_var->name_len = name_len;
2457 tmp_var->str_val_len = val_len;
2458 tmp_var->alloced_len = val_alloc_len;
2459 return tmp_var;
2460}
2461
2462
2463VAR* var_from_env(const char *name, const char *def_val)
2464{
2465 const char *tmp;
2466 VAR *v;
2467 if (!(tmp = getenv(name)))
2468 tmp = def_val;
2469
2470 v = var_init(0, name, strlen(name), tmp, strlen(tmp));
2471 my_hash_insert(&var_hash, (uchar*)v);
2472 return v;
2473}
2474
2475
2476VAR* var_get(const char *var_name, const char **var_name_end, my_bool raw,
2477 my_bool ignore_not_existing)
2478{
2479 int digit;
2480 VAR *v;
2481 DBUG_ENTER("var_get");
2482 DBUG_PRINT("enter", ("var_name: %s",var_name));
2483
2484 if (*var_name != '$')
2485 goto err;
2486 digit = *++var_name - '0';
2487 if (digit < 0 || digit >= 10)
2488 {
2489 const char *save_var_name = var_name, *end;
2490 uint length;
2491 end = (var_name_end) ? *var_name_end : 0;
2492 while (my_isvar(charset_info,*var_name) && var_name != end)
2493 var_name++;
2494 if (var_name == save_var_name)
2495 {
2496 if (ignore_not_existing)
2497 DBUG_RETURN(0);
2498 die("Empty variable");
2499 }
2500 length= (uint) (var_name - save_var_name);
2501 if (length >= MAX_VAR_NAME_LENGTH)
2502 die("Too long variable name: %s", save_var_name);
2503
2504 if (!(v = (VAR*) my_hash_search(&var_hash, (const uchar*) save_var_name,
2505 length)))
2506 {
2507 char buff[MAX_VAR_NAME_LENGTH+1];
2508 strmake(buff, save_var_name, length);
2509 v= var_from_env(buff, "");
2510 }
2511 var_name--; /* Point at last character */
2512 }
2513 else
2514 v = var_reg + digit;
2515
2516 if (!raw && v->int_dirty)
2517 {
2518 sprintf(v->str_val, "%d", v->int_val);
2519 v->int_dirty= false;
2520 v->str_val_len = strlen(v->str_val);
2521 }
2522 if (var_name_end)
2523 *var_name_end = var_name ;
2524 DBUG_RETURN(v);
2525err:
2526 if (var_name_end)
2527 *var_name_end = 0;
2528 die("Unsupported variable name: %s", var_name);
2529 DBUG_RETURN(0);
2530}
2531
2532
2533VAR *var_obtain(const char *name, int len)
2534{
2535 VAR* v;
2536 if ((v = (VAR*)my_hash_search(&var_hash, (const uchar *) name, len)))
2537 return v;
2538 v = var_init(0, name, len, "", 0);
2539 my_hash_insert(&var_hash, (uchar*)v);
2540 return v;
2541}
2542
2543
2544/*
2545 - if variable starts with a $ it is regarded as a local test variable
2546 - if not it is treated as a environment variable, and the corresponding
2547 environment variable will be updated
2548*/
2549
2550void var_set(const char *var_name, const char *var_name_end,
2551 const char *var_val, const char *var_val_end)
2552{
2553 int digit, env_var= 0;
2554 VAR *v;
2555 DBUG_ENTER("var_set");
2556 DBUG_PRINT("enter", ("var_name: '%.*s' = '%.*s' (length: %d)",
2557 (int) (var_name_end - var_name), var_name,
2558 (int) (var_val_end - var_val), var_val,
2559 (int) (var_val_end - var_val)));
2560
2561 if (*var_name != '$')
2562 env_var= 1;
2563 else
2564 var_name++;
2565
2566 digit= *var_name - '0';
2567 if (!(digit < 10 && digit >= 0))
2568 {
2569 v= var_obtain(var_name, (uint) (var_name_end - var_name));
2570 }
2571 else
2572 v= var_reg + digit;
2573
2574 eval_expr(v, var_val, (const char**) &var_val_end);
2575
2576 if (env_var)
2577 {
2578 if (v->int_dirty)
2579 {
2580 sprintf(v->str_val, "%d", v->int_val);
2581 v->int_dirty=false;
2582 v->str_val_len= strlen(v->str_val);
2583 }
2584 /* setenv() expects \0-terminated strings */
2585 DBUG_ASSERT(v->name[v->name_len] == 0);
2586 setenv(v->name, v->str_val, 1);
2587 }
2588 DBUG_VOID_RETURN;
2589}
2590
2591
2592void var_set_string(const char* name, const char* value)
2593{
2594 var_set(name, name + strlen(name), value, value + strlen(value));
2595}
2596
2597
2598void var_set_int(const char* name, int value)
2599{
2600 char buf[21];
2601 my_snprintf(buf, sizeof(buf), "%d", value);
2602 var_set_string(name, buf);
2603}
2604
2605
2606/*
2607 Store an integer (typically the returncode of the last SQL)
2608 statement in the mysqltest builtin variable $mysql_errno
2609*/
2610
2611void var_set_errno(int sql_errno)
2612{
2613 var_set_int("$mysql_errno", sql_errno);
2614 var_set_string("$mysql_errname", get_errname_from_code(sql_errno));
2615}
2616
2617/* Functions to handle --disable and --enable properties */
2618
2619void set_once_property(enum_prop prop, my_bool val)
2620{
2621 property &pr= prop_list[prop];
2622 pr.set= 1;
2623 pr.old= *pr.var;
2624 *pr.var= val;
2625 var_set_int(pr.env_name, (val != pr.reverse));
2626 once_property= TRUE;
2627}
2628
2629void set_property(st_command *command, enum_prop prop, my_bool val)
2630{
2631 char* p= command->first_argument;
2632 if (p && !strcmp (p, "ONCE"))
2633 {
2634 command->last_argument= p + 4;
2635 set_once_property(prop, val);
2636 return;
2637 }
2638 property &pr= prop_list[prop];
2639 *pr.var= val;
2640 pr.set= 0;
2641 var_set_int(pr.env_name, (val != pr.reverse));
2642}
2643
2644void revert_properties()
2645{
2646 if (! once_property)
2647 return;
2648 for (int i= 0; i < (int) P_MAX; i++)
2649 {
2650 property &pr= prop_list[i];
2651 if (pr.set)
2652 {
2653 *pr.var= pr.old;
2654 pr.set= 0;
2655 var_set_int(pr.env_name, (pr.old != pr.reverse));
2656 }
2657 }
2658 once_property=FALSE;
2659}
2660
2661
2662/*
2663 Set variable from the result of a query
2664
2665 SYNOPSIS
2666 var_query_set()
2667 var variable to set from query
2668 query start of query string to execute
2669 query_end end of the query string to execute
2670
2671
2672 DESCRIPTION
2673 let @<var_name> = `<query>`
2674
2675 Execute the query and assign the first row of result to var as
2676 a tab separated strings
2677
2678 Also assign each column of the result set to
2679 variable "$<var_name>_<column_name>"
2680 Thus the tab separated output can be read from $<var_name> and
2681 and each individual column can be read as $<var_name>_<col_name>
2682
2683*/
2684
2685void var_query_set(VAR *var, const char *query, const char** query_end)
2686{
2687 char *end = (char*)((query_end && *query_end) ?
2688 *query_end : query + strlen(query));
2689 MYSQL_RES *UNINIT_VAR(res);
2690 MYSQL_ROW row;
2691 MYSQL* mysql = cur_con->mysql;
2692 DYNAMIC_STRING ds_query;
2693 DBUG_ENTER("var_query_set");
2694
2695 if (!mysql)
2696 {
2697 struct st_command command;
2698 DBUG_ASSERT(query_end);
2699 memset(&command, 0, sizeof(command));
2700 command.query= (char*)query;
2701 command.first_word_len= (int)(*query_end - query);
2702 command.first_argument= command.query + command.first_word_len;
2703 command.end= (char*)*query_end;
2704 command.abort_on_error= 1; /* avoid uninitialized variables */
2705 handle_no_active_connection(&command, cur_con, &ds_res);
2706 DBUG_VOID_RETURN;
2707 }
2708
2709 /* Only white space or ) allowed past ending ` */
2710 while (end > query && *end != '`')
2711 {
2712 if (*end && (*end != ' ' && *end != '\t' && *end != '\n' && *end != ')'))
2713 die("Spurious text after `query` expression");
2714 --end;
2715 }
2716
2717 if (query == end)
2718 die("Syntax error in query, missing '`'");
2719 ++query;
2720
2721 /* Eval the query, thus replacing all environment variables */
2722 init_dynamic_string(&ds_query, 0, (end - query) + 32, 256);
2723 do_eval(&ds_query, query, end, FALSE);
2724
2725 if (mysql_real_query(mysql, ds_query.str, (ulong)ds_query.length) ||
2726 !(res= mysql_store_result(mysql)))
2727 {
2728 handle_error(curr_command, mysql_errno(mysql), mysql_error(mysql),
2729 mysql_sqlstate(mysql), &ds_res);
2730 /* If error was acceptable, return empty string */
2731 dynstr_free(&ds_query);
2732 eval_expr(var, "", 0);
2733 DBUG_VOID_RETURN;
2734 }
2735
2736 dynstr_free(&ds_query);
2737
2738 if ((row= mysql_fetch_row(res)) && row[0])
2739 {
2740 /*
2741 Concatenate all fields in the first row with tab in between
2742 and assign that string to the $variable
2743 */
2744 DYNAMIC_STRING result;
2745 uint i;
2746 ulong *lengths;
2747
2748 init_dynamic_string(&result, "", 512, 512);
2749 lengths= mysql_fetch_lengths(res);
2750 for (i= 0; i < mysql_num_fields(res); i++)
2751 {
2752 if (row[i])
2753 {
2754 /* Add column to tab separated string */
2755 char *val= row[i];
2756 size_t len= lengths[i];
2757
2758 if (glob_replace_regex)
2759 {
2760 /* Regex replace */
2761 if (!multi_reg_replace(glob_replace_regex, (char*)val))
2762 {
2763 val= glob_replace_regex->buf;
2764 len= strlen(val);
2765 }
2766 }
2767
2768 if (glob_replace)
2769 replace_strings_append(glob_replace, &result, val);
2770 else
2771 dynstr_append_mem(&result, val, len);
2772 }
2773 dynstr_append_mem(&result, "\t", 1);
2774 }
2775 end= result.str + result.length-1;
2776 /* Evaluation should not recurse via backtick */
2777 eval_expr(var, result.str, (const char**) &end, false, false);
2778 dynstr_free(&result);
2779 }
2780 else
2781 eval_expr(var, "", 0);
2782
2783 mysql_free_result(res);
2784 DBUG_VOID_RETURN;
2785}
2786
2787
2788static void
2789set_result_format_version(ulong new_version)
2790{
2791 switch (new_version){
2792 case 1:
2793 /* The first format */
2794 break;
2795 case 2:
2796 /* New format that also writes comments and empty lines
2797 from test file to result */
2798 break;
2799 default:
2800 die("Version format %lu has not yet been implemented", new_version);
2801 break;
2802 }
2803 opt_result_format_version= new_version;
2804}
2805
2806
2807/*
2808 Set the result format version to use when generating
2809 the .result file
2810*/
2811
2812static void
2813do_result_format_version(struct st_command *command)
2814{
2815 long version;
2816 static DYNAMIC_STRING ds_version;
2817 const struct command_arg result_format_args[] = {
2818 {"version", ARG_STRING, TRUE, &ds_version, "Version to use"}
2819 };
2820
2821 DBUG_ENTER("do_result_format_version");
2822
2823 check_command_args(command, command->first_argument,
2824 result_format_args,
2825 sizeof(result_format_args)/sizeof(struct command_arg),
2826 ',');
2827
2828 /* Convert version number to int */
2829 if (!str2int(ds_version.str, 10, (long) 0, (long) INT_MAX, &version))
2830 die("Invalid version number: '%s'", ds_version.str);
2831
2832 set_result_format_version(version);
2833
2834 dynstr_append(&ds_res, "result_format: ");
2835 dynstr_append_mem(&ds_res, ds_version.str, ds_version.length);
2836 dynstr_append(&ds_res, "\n");
2837 dynstr_free(&ds_version);
2838}
2839
2840
2841/*
2842 Set variable from the result of a field in a query
2843
2844 This function is useful when checking for a certain value
2845 in the output from a query that can't be restricted to only
2846 return some values. A very good example of that is most SHOW
2847 commands.
2848
2849 SYNOPSIS
2850 var_set_query_get_value()
2851
2852 DESCRIPTION
2853 let $variable= query_get_value(<query to run>,<column name>,<row no>);
2854
2855 <query to run> - The query that should be sent to the server
2856 <column name> - Name of the column that holds the field be compared
2857 against the expected value
2858 <row no> - Number of the row that holds the field to be
2859 compared against the expected value
2860
2861*/
2862
2863void var_set_query_get_value(struct st_command *command, VAR *var)
2864{
2865 long row_no;
2866 int col_no= -1;
2867 MYSQL_RES* UNINIT_VAR(res);
2868 MYSQL* mysql= cur_con->mysql;
2869
2870 static DYNAMIC_STRING ds_query;
2871 static DYNAMIC_STRING ds_col;
2872 static DYNAMIC_STRING ds_row;
2873 const struct command_arg query_get_value_args[] = {
2874 {"query", ARG_STRING, TRUE, &ds_query, "Query to run"},
2875 {"column name", ARG_STRING, TRUE, &ds_col, "Name of column"},
2876 {"row number", ARG_STRING, TRUE, &ds_row, "Number for row"}
2877 };
2878
2879 DBUG_ENTER("var_set_query_get_value");
2880
2881 if (!mysql)
2882 {
2883 handle_no_active_connection(command, cur_con, &ds_res);
2884 DBUG_VOID_RETURN;
2885 }
2886
2887 strip_parentheses(command);
2888 DBUG_PRINT("info", ("query: %s", command->query));
2889 check_command_args(command, command->first_argument, query_get_value_args,
2890 sizeof(query_get_value_args)/sizeof(struct command_arg),
2891 ',');
2892
2893 DBUG_PRINT("info", ("query: %s", ds_query.str));
2894 DBUG_PRINT("info", ("col: %s", ds_col.str));
2895
2896 /* Convert row number to int */
2897 if (!str2int(ds_row.str, 10, (long) 0, (long) INT_MAX, &row_no))
2898 die("Invalid row number: '%s'", ds_row.str);
2899 DBUG_PRINT("info", ("row: %s, row_no: %ld", ds_row.str, row_no));
2900 dynstr_free(&ds_row);
2901
2902 /* Remove any surrounding "'s from the query - if there is any */
2903 if (strip_surrounding(ds_query.str, '"', '"'))
2904 die("Mismatched \"'s around query '%s'", ds_query.str);
2905
2906 /* Run the query */
2907 if (mysql_real_query(mysql, ds_query.str, (ulong)ds_query.length))
2908 {
2909 handle_error(curr_command, mysql_errno(mysql), mysql_error(mysql),
2910 mysql_sqlstate(mysql), &ds_res);
2911 /* If error was acceptable, return empty string */
2912 dynstr_free(&ds_query);
2913 dynstr_free(&ds_col);
2914 eval_expr(var, "", 0);
2915 DBUG_VOID_RETURN;
2916 }
2917
2918 if (!(res= mysql_store_result(mysql)))
2919 {
2920 report_or_die("Query '%s' didn't return a result set", ds_query.str);
2921 dynstr_free(&ds_query);
2922 dynstr_free(&ds_col);
2923 eval_expr(var, "", 0);
2924 DBUG_VOID_RETURN;
2925 }
2926
2927 {
2928 /* Find column number from the given column name */
2929 uint i;
2930 uint num_fields= mysql_num_fields(res);
2931 MYSQL_FIELD *fields= mysql_fetch_fields(res);
2932
2933 for (i= 0; i < num_fields; i++)
2934 {
2935 if (strcmp(fields[i].name, ds_col.str) == 0 &&
2936 strlen(fields[i].name) == ds_col.length)
2937 {
2938 col_no= i;
2939 break;
2940 }
2941 }
2942 if (col_no == -1)
2943 {
2944 mysql_free_result(res);
2945 report_or_die("Could not find column '%s' in the result of '%s'",
2946 ds_col.str, ds_query.str);
2947 dynstr_free(&ds_query);
2948 dynstr_free(&ds_col);
2949 DBUG_VOID_RETURN;
2950 }
2951 DBUG_PRINT("info", ("Found column %d with name '%s'",
2952 i, fields[i].name));
2953 }
2954 dynstr_free(&ds_col);
2955
2956 {
2957 /* Get the value */
2958 MYSQL_ROW row;
2959 long rows= 0;
2960 const char* value= "No such row";
2961
2962 while ((row= mysql_fetch_row(res)))
2963 {
2964 if (++rows == row_no)
2965 {
2966
2967 DBUG_PRINT("info", ("At row %ld, column %d is '%s'",
2968 row_no, col_no, row[col_no]));
2969 /* Found the row to get */
2970 if (row[col_no])
2971 value= row[col_no];
2972 else
2973 value= "NULL";
2974
2975 break;
2976 }
2977 }
2978 eval_expr(var, value, 0, false, false);
2979 }
2980 dynstr_free(&ds_query);
2981 mysql_free_result(res);
2982
2983 DBUG_VOID_RETURN;
2984}
2985
2986
2987void var_copy(VAR *dest, VAR *src)
2988{
2989 dest->int_val= src->int_val;
2990 dest->is_int= src->is_int;
2991 dest->int_dirty= src->int_dirty;
2992
2993 /* Alloc/realloc data for str_val in dest */
2994 if (dest->alloced_len < src->alloced_len &&
2995 !(dest->str_val= dest->str_val
2996 ? (char*)my_realloc(dest->str_val, src->alloced_len, MYF(MY_WME))
2997 : (char*)my_malloc(src->alloced_len, MYF(MY_WME))))
2998 die("Out of memory");
2999 else
3000 dest->alloced_len= src->alloced_len;
3001
3002 /* Copy str_val data to dest */
3003 dest->str_val_len= src->str_val_len;
3004 if (src->str_val_len)
3005 memcpy(dest->str_val, src->str_val, src->str_val_len);
3006}
3007
3008
3009void eval_expr(VAR *v, const char *p, const char **p_end,
3010 bool open_end, bool do_eval)
3011{
3012
3013 DBUG_ENTER("eval_expr");
3014 DBUG_PRINT("enter", ("p: '%s'", p));
3015
3016 /* Skip to treat as pure string if no evaluation */
3017 if (! do_eval)
3018 goto NO_EVAL;
3019
3020 if (*p == '$')
3021 {
3022 VAR *vp;
3023 const char* expected_end= *p_end; // Remember var end
3024 if ((vp= var_get(p, p_end, 0, 0)))
3025 var_copy(v, vp);
3026
3027 /* Apparently it is not safe to assume null-terminated string */
3028 v->str_val[v->str_val_len]= 0;
3029
3030 /* Make sure there was just a $variable and nothing else */
3031 const char* end= *p_end + 1;
3032 if (end < expected_end && !open_end)
3033 die("Found junk '%.*s' after $variable in expression",
3034 (int)(expected_end - end - 1), end);
3035
3036 DBUG_VOID_RETURN;
3037 }
3038
3039 if (*p == '`')
3040 {
3041 var_query_set(v, p, p_end);
3042 DBUG_VOID_RETURN;
3043 }
3044
3045 {
3046 /* Check if this is a "let $var= query_get_value()" */
3047 const char* get_value_str= "query_get_value";
3048 const size_t len= strlen(get_value_str);
3049 if (strncmp(p, get_value_str, len)==0)
3050 {
3051 struct st_command command;
3052 memset(&command, 0, sizeof(command));
3053 command.query= (char*)p;
3054 command.first_word_len= (int)len;
3055 command.first_argument= command.query + len;
3056 command.end= (char*)*p_end;
3057 command.abort_on_error= 1; /* avoid uninitialized variables */
3058 var_set_query_get_value(&command, v);
3059 DBUG_VOID_RETURN;
3060 }
3061 }
3062
3063 NO_EVAL:
3064 {
3065 size_t new_val_len = (p_end && *p_end) ?
3066 (size_t)(*p_end - p) : strlen(p);
3067 if (new_val_len + 1 >= v->alloced_len)
3068 {
3069 static size_t MIN_VAR_ALLOC= 32;
3070 v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
3071 MIN_VAR_ALLOC : new_val_len + 1;
3072 if (!(v->str_val =
3073 v->str_val ?
3074 (char*)my_realloc(v->str_val, v->alloced_len+1, MYF(MY_WME)) :
3075 (char*)my_malloc(v->alloced_len+1, MYF(MY_WME))))
3076 die("Out of memory");
3077 }
3078 v->str_val_len = new_val_len;
3079 memcpy(v->str_val, p, new_val_len);
3080 v->str_val[new_val_len] = 0;
3081 var_check_int(v);
3082 }
3083 DBUG_VOID_RETURN;
3084}
3085
3086
3087bool open_and_set_current(const char *name)
3088{
3089 FILE *opened= fopen(name, "rb");
3090
3091 if (!opened)
3092 return false;
3093
3094 cur_file++;
3095 cur_file->file= opened;
3096 cur_file->file_name= my_strdup(name, MYF(MY_FAE));
3097 cur_file->lineno=1;
3098 return true;
3099}
3100
3101
3102void open_file(const char *name)
3103{
3104 char buff[FN_REFLEN];
3105 size_t length;
3106 char *curname= cur_file->file_name;
3107 DBUG_ENTER("open_file");
3108 DBUG_PRINT("enter", ("name: %s", name));
3109
3110 if (cur_file == file_stack_end)
3111 die("Source directives are nesting too deep");
3112
3113 if (test_if_hard_path(name))
3114 {
3115 if (open_and_set_current(name))
3116 DBUG_VOID_RETURN;
3117 }
3118 else
3119 {
3120 /*
3121 if overlay-dir is specified, and the file is located somewhere
3122 under overlay-dir or under suite-dir, the search works as follows:
3123
3124 0.let suffix be current file dirname relative to siute-dir or overlay-dir
3125 1.try in overlay-dir/suffix
3126 2.try in suite-dir/suffix
3127 3.try in overlay-dir
3128 4.try in suite-dir
3129 5.try in basedir
3130
3131 consider an example: 'rty' overlay of the 'qwe' suite,
3132 file qwe/include/some.inc contains the line
3133 --source thing.inc
3134 we look for it in this order:
3135 0.suffix is "include/"
3136 1.try in rty/include/thing.inc
3137 2.try in qwe/include/thing.inc
3138 3.try in try/thing.inc | this is useful when t/a.test has
3139 4.try in qwe/thing.inc | source include/b.inc;
3140 5.try in mysql-test/include/thing.inc
3141
3142 otherwise the search is as follows
3143 1.try in current file dirname
3144 3.try in overlay-dir (if any)
3145 4.try in suite-dir
3146 5.try in basedir
3147 */
3148
3149 fix_win_paths(curname, sizeof(curname));
3150
3151 bool in_overlay= opt_overlay_dir &&
3152 !strncmp(curname, opt_overlay_dir, overlay_dir_len);
3153 bool in_suiteir= opt_overlay_dir && !in_overlay &&
3154 !strncmp(curname, opt_suite_dir, suite_dir_len);
3155 if (in_overlay || in_suiteir)
3156 {
3157 size_t prefix_len = in_overlay ? overlay_dir_len : suite_dir_len;
3158 char buf2[FN_REFLEN], *suffix= buf2 + prefix_len;
3159 dirname_part(buf2, curname, &length);
3160
3161 /* 1. first we look in the overlay dir */
3162 strxnmov(buff, sizeof(buff), opt_overlay_dir, suffix, name, NullS);
3163
3164 /*
3165 Overlayed rty/include/thing.inc can contain the line
3166 --source thing.inc
3167 which would mean to include qwe/include/thing.inc.
3168 But it looks like including "itself", so don't try to open the file,
3169 if buff contains the same file name as curname.
3170 */
3171 if (strcmp(buff, curname) && open_and_set_current(buff))
3172 DBUG_VOID_RETURN;
3173
3174 /* 2. if that failed, we look in the suite dir */
3175 strxnmov(buff, sizeof(buff), opt_suite_dir, suffix, name, NullS);
3176
3177 /* buff can not be equal to curname, as a file can never include itself */
3178 if (open_and_set_current(buff))
3179 DBUG_VOID_RETURN;
3180 }
3181 else
3182 {
3183 /* 1. try in current file dirname */
3184 dirname_part(buff, curname, &length);
3185 strxnmov(buff, sizeof(buff), buff, name, NullS);
3186 if (open_and_set_current(buff))
3187 DBUG_VOID_RETURN;
3188 }
3189
3190 /* 3. now, look in the overlay dir */
3191 if (opt_overlay_dir)
3192 {
3193 strxmov(buff, opt_overlay_dir, name, NullS);
3194 if (open_and_set_current(buff))
3195 DBUG_VOID_RETURN;
3196 }
3197
3198 /* 4. if that failed - look in the suite dir */
3199 strxmov(buff, opt_suite_dir, name, NullS);
3200 if (open_and_set_current(buff))
3201 DBUG_VOID_RETURN;
3202
3203 /* 5. the last resort - look in the base dir */
3204 strxnmov(buff, sizeof(buff), opt_basedir, name, NullS);
3205 if (open_and_set_current(buff))
3206 DBUG_VOID_RETURN;
3207 }
3208
3209 die("Could not open '%s' for reading, errno: %d", name, errno);
3210 DBUG_VOID_RETURN;
3211}
3212
3213
3214/*
3215 Source and execute the given file
3216
3217 SYNOPSIS
3218 do_source()
3219 query called command
3220
3221 DESCRIPTION
3222 source <file_name>
3223
3224 Open the file <file_name> and execute it
3225
3226*/
3227
3228void do_source(struct st_command *command)
3229{
3230 static DYNAMIC_STRING ds_filename;
3231 const struct command_arg source_args[] = {
3232 { "filename", ARG_STRING, TRUE, &ds_filename, "File to source" }
3233 };
3234 DBUG_ENTER("do_source");
3235
3236 check_command_args(command, command->first_argument, source_args,
3237 sizeof(source_args)/sizeof(struct command_arg),
3238 ' ');
3239
3240 /*
3241 If this file has already been sourced, don't source it again.
3242 It's already available in the q_lines cache.
3243 */
3244 if (parser.current_line < (parser.read_lines - 1))
3245 ; /* Do nothing */
3246 else
3247 {
3248 DBUG_PRINT("info", ("sourcing file: %s", ds_filename.str));
3249 open_file(ds_filename.str);
3250 }
3251
3252 dynstr_free(&ds_filename);
3253 DBUG_VOID_RETURN;
3254}
3255
3256
3257#if defined _WIN32
3258
3259#ifdef USE_CYGWIN
3260/* Variables used for temporary sh files used for emulating Unix on Windows */
3261char tmp_sh_name[64], tmp_sh_cmd[70];
3262#endif
3263
3264void init_tmp_sh_file()
3265{
3266#ifdef USE_CYGWIN
3267 /* Format a name for the tmp sh file that is unique for this process */
3268 my_snprintf(tmp_sh_name, sizeof(tmp_sh_name), "tmp_%d.sh", getpid());
3269 /* Format the command to execute in order to run the script */
3270 my_snprintf(tmp_sh_cmd, sizeof(tmp_sh_cmd), "sh %s", tmp_sh_name);
3271#endif
3272}
3273
3274
3275void free_tmp_sh_file()
3276{
3277#ifdef USE_CYGWIN
3278 my_delete(tmp_sh_name, MYF(0));
3279#endif
3280}
3281#endif
3282
3283
3284static void init_builtin_echo(void)
3285{
3286#ifdef _WIN32
3287 size_t echo_length;
3288
3289 /* Look for "echo.exe" in same dir as mysqltest was started from */
3290 dirname_part(builtin_echo, my_progname, &echo_length);
3291 fn_format(builtin_echo, ".\\echo.exe",
3292 builtin_echo, "", MYF(MY_REPLACE_DIR));
3293
3294 /* Make sure echo.exe exists */
3295 if (access(builtin_echo, F_OK) != 0)
3296 builtin_echo[0]= 0;
3297 return;
3298
3299#else
3300
3301 builtin_echo[0]= 0;
3302 return;
3303
3304#endif
3305}
3306
3307
3308/*
3309 Replace a substring
3310
3311 SYNOPSIS
3312 replace
3313 ds_str The string to search and perform the replace in
3314 search_str The string to search for
3315 search_len Length of the string to search for
3316 replace_str The string to replace with
3317 replace_len Length of the string to replace with
3318
3319 RETURN
3320 0 String replaced
3321 1 Could not find search_str in str
3322*/
3323
3324static int replace(DYNAMIC_STRING *ds_str,
3325 const char *search_str, size_t search_len,
3326 const char *replace_str, size_t replace_len)
3327{
3328 DYNAMIC_STRING ds_tmp;
3329 const char *start= strstr(ds_str->str, search_str);
3330 if (!start)
3331 return 1;
3332 init_dynamic_string(&ds_tmp, "",
3333 ds_str->length + replace_len, 256);
3334 dynstr_append_mem(&ds_tmp, ds_str->str, start - ds_str->str);
3335 dynstr_append_mem(&ds_tmp, replace_str, replace_len);
3336 dynstr_append(&ds_tmp, start + search_len);
3337 dynstr_set(ds_str, ds_tmp.str);
3338 dynstr_free(&ds_tmp);
3339 return 0;
3340}
3341
3342
3343/*
3344 Execute given command.
3345
3346 SYNOPSIS
3347 do_exec()
3348 query called command
3349
3350 DESCRIPTION
3351 exec <command>
3352
3353 Execute the text between exec and end of line in a subprocess.
3354 The error code returned from the subprocess is checked against the
3355 expected error array, previously set with the --error command.
3356 It can thus be used to execute a command that shall fail.
3357
3358 NOTE
3359 Although mysqltest is executed from cygwin shell, the command will be
3360 executed in "cmd.exe". Thus commands like "rm" etc can NOT be used, use
3361 mysqltest command(s) like "remove_file" for that
3362*/
3363
3364void do_exec(struct st_command *command)
3365{
3366 int error;
3367 char buf[512];
3368 FILE *res_file;
3369 char *cmd= command->first_argument;
3370 DYNAMIC_STRING ds_cmd;
3371 DYNAMIC_STRING ds_sorted, *ds_result;
3372 DBUG_ENTER("do_exec");
3373 DBUG_PRINT("enter", ("cmd: '%s'", cmd));
3374
3375 var_set_int("$sys_errno",0);
3376
3377 /* Skip leading space */
3378 while (*cmd && my_isspace(charset_info, *cmd))
3379 cmd++;
3380 if (!*cmd)
3381 {
3382 report_or_die("Missing argument in exec");
3383 DBUG_VOID_RETURN;
3384 }
3385 command->last_argument= command->end;
3386
3387 init_dynamic_string(&ds_cmd, 0, command->query_len+256, 256);
3388 /* Eval the command, thus replacing all environment variables */
3389 do_eval(&ds_cmd, cmd, command->end, !is_windows);
3390
3391 /* Check if echo should be replaced with "builtin" echo */
3392 if (builtin_echo[0] && strncmp(cmd, "echo", 4) == 0)
3393 {
3394 /* Replace echo with our "builtin" echo */
3395 replace(&ds_cmd, "echo", 4, builtin_echo, strlen(builtin_echo));
3396 }
3397
3398#ifdef _WIN32
3399#ifndef USE_CYGWIN
3400 /* Replace /dev/null with NUL */
3401 while(replace(&ds_cmd, "/dev/null", 9, "NUL", 3) == 0)
3402 ;
3403 /* Replace "closed stdout" with non existing output fd */
3404 while(replace(&ds_cmd, ">&-", 3, ">&4", 3) == 0)
3405 ;
3406#endif
3407#endif
3408
3409 if (disable_result_log)
3410 {
3411 /* Collect stderr output as well, for the case app. crashes or returns error.*/
3412 dynstr_append(&ds_cmd, " 2>&1");
3413 }
3414
3415 DBUG_PRINT("info", ("Executing '%s' as '%s'",
3416 command->first_argument, ds_cmd.str));
3417
3418 if (!(res_file= my_popen(ds_cmd.str, "r")))
3419 {
3420 dynstr_free(&ds_cmd);
3421 if (command->abort_on_error)
3422 report_or_die("popen(\"%s\", \"r\") failed", command->first_argument);
3423 DBUG_VOID_RETURN;
3424 }
3425
3426 ds_result= &ds_res;
3427 if (display_result_sorted)
3428 {
3429 init_dynamic_string(&ds_sorted, "", 1024, 1024);
3430 ds_result= &ds_sorted;
3431 }
3432 int len;
3433 while (my_fgets(buf, sizeof(buf), res_file,&len))
3434 {
3435 replace_dynstr_append_mem(ds_result, buf, len);
3436 }
3437 error= pclose(res_file);
3438
3439 if (display_result_sorted)
3440 {
3441 dynstr_append_sorted(&ds_res, &ds_sorted, 0);
3442 dynstr_free(&ds_sorted);
3443 }
3444
3445 if (error)
3446 {
3447 uint status= WEXITSTATUS(error);
3448 int i;
3449
3450 if (command->abort_on_error)
3451 {
3452 report_or_die("exec of '%s' failed, error: %d, status: %d, errno: %d\n"
3453 "Output from before failure:\n%s\n",
3454 ds_cmd.str, error, status, errno,
3455 ds_res.str);
3456 dynstr_free(&ds_cmd);
3457 DBUG_VOID_RETURN;
3458 }
3459
3460 DBUG_PRINT("info",
3461 ("error: %d, status: %d", error, status));
3462
3463 i= match_expected_error(command, status, NULL);
3464
3465 if (i >= 0)
3466 DBUG_PRINT("info", ("command \"%s\" failed with expected error: %d",
3467 command->first_argument, status));
3468 else
3469 {
3470 dynstr_free(&ds_cmd);
3471 if (command->expected_errors.count > 0)
3472 report_or_die("command \"%s\" failed with wrong error: %d",
3473 command->first_argument, status);
3474 }
3475 var_set_int("$sys_errno",status);
3476 }
3477 else if (command->expected_errors.err[0].type == ERR_ERRNO &&
3478 command->expected_errors.err[0].code.errnum != 0)
3479 {
3480 /* Error code we wanted was != 0, i.e. not an expected success */
3481 log_msg("exec of '%s failed, error: %d, errno: %d",
3482 ds_cmd.str, error, errno);
3483 dynstr_free(&ds_cmd);
3484 report_or_die("command \"%s\" succeeded - should have failed with "
3485 "errno %d...",
3486 command->first_argument,
3487 command->expected_errors.err[0].code.errnum);
3488 }
3489
3490 dynstr_free(&ds_cmd);
3491
3492 if (disable_result_log)
3493 {
3494 /* Disable output in case of successful exit.*/
3495 dynstr_set(&ds_res,"");
3496 }
3497 DBUG_VOID_RETURN;
3498}
3499
3500enum enum_operator
3501{
3502 DO_DEC,
3503 DO_INC
3504};
3505
3506
3507/*
3508 Decrease or increase the value of a variable
3509
3510 SYNOPSIS
3511 do_modify_var()
3512 query called command
3513 op operation to perform on the var
3514
3515 DESCRIPTION
3516 dec $var_name
3517 inc $var_name
3518
3519*/
3520
3521int do_modify_var(struct st_command *command,
3522 enum enum_operator op)
3523{
3524 const char *p= command->first_argument;
3525 VAR* v;
3526 if (!*p)
3527 die("Missing argument to %.*s", command->first_word_len,
3528 command->query);
3529 if (*p != '$')
3530 die("The argument to %.*s must be a variable (start with $)",
3531 command->first_word_len, command->query);
3532 v= var_get(p, &p, 1, 0);
3533 if (! v->is_int)
3534 die("Cannot perform inc/dec on a non-numeric value");
3535 switch (op) {
3536 case DO_DEC:
3537 v->int_val--;
3538 break;
3539 case DO_INC:
3540 v->int_val++;
3541 break;
3542 default:
3543 die("Invalid operator to do_modify_var");
3544 break;
3545 }
3546 v->int_dirty= true;
3547 command->last_argument= (char*)++p;
3548 return 0;
3549}
3550
3551
3552/*
3553 Wrapper for 'system' function
3554
3555 NOTE
3556 If mysqltest is executed from cygwin shell, the command will be
3557 executed in the "windows command interpreter" cmd.exe and we prepend "sh"
3558 to make it be executed by cygwins "bash". Thus commands like "rm",
3559 "mkdir" as well as shellscripts can executed by "system" in Windows.
3560
3561*/
3562
3563int my_system(DYNAMIC_STRING* ds_cmd)
3564{
3565#if defined _WIN32 && defined USE_CYGWIN
3566 /* Dump the command into a sh script file and execute with system */
3567 str_to_file(tmp_sh_name, ds_cmd->str, ds_cmd->length);
3568 return system(tmp_sh_cmd);
3569#else
3570 return system(ds_cmd->str);
3571#endif
3572}
3573
3574
3575/*
3576 SYNOPSIS
3577 do_system
3578 command called command
3579
3580 DESCRIPTION
3581 system <command>
3582
3583 Eval the query to expand any $variables in the command.
3584 Execute the command with the "system" command.
3585
3586*/
3587
3588void do_system(struct st_command *command)
3589{
3590 DYNAMIC_STRING ds_cmd;
3591 DBUG_ENTER("do_system");
3592
3593 if (strlen(command->first_argument) == 0)
3594 {
3595 report_or_die("Missing arguments to system, nothing to do!");
3596 DBUG_VOID_RETURN;
3597 }
3598
3599 init_dynamic_string(&ds_cmd, 0, command->query_len + 64, 256);
3600
3601 /* Eval the system command, thus replacing all environment variables */
3602 do_eval(&ds_cmd, command->first_argument, command->end, !is_windows);
3603
3604#ifdef _WIN32
3605#ifndef USE_CYGWIN
3606 /* Replace /dev/null with NUL */
3607 while(replace(&ds_cmd, "/dev/null", 9, "NUL", 3) == 0)
3608 ;
3609#endif
3610#endif
3611
3612
3613 DBUG_PRINT("info", ("running system command '%s' as '%s'",
3614 command->first_argument, ds_cmd.str));
3615 if (my_system(&ds_cmd))
3616 {
3617 if (command->abort_on_error)
3618 report_or_die("system command '%s' failed", command->first_argument);
3619 else
3620 {
3621 /* If ! abort_on_error, log message and continue */
3622 dynstr_append(&ds_res, "system command '");
3623 replace_dynstr_append(&ds_res, command->first_argument);
3624 dynstr_append(&ds_res, "' failed\n");
3625 }
3626 }
3627
3628 command->last_argument= command->end;
3629 dynstr_free(&ds_cmd);
3630 DBUG_VOID_RETURN;
3631}
3632
3633
3634/* returns TRUE if path is inside a sandbox */
3635bool is_sub_path(const char *path, size_t plen, const char *sandbox)
3636{
3637 size_t len= strlen(sandbox);
3638 if (!sandbox || !len || plen <= len || memcmp(path, sandbox, len - 1)
3639 || path[len] != '/')
3640 return false;
3641 return true;
3642}
3643
3644
3645/* returns TRUE if path cannot be modified */
3646bool bad_path(const char *path)
3647{
3648 size_t plen= strlen(path);
3649
3650 const char *vardir= getenv("MYSQLTEST_VARDIR");
3651 if (is_sub_path(path, plen, vardir))
3652 return false;
3653
3654 const char *tmpdir= getenv("MYSQL_TMP_DIR");
3655 if (is_sub_path(path, plen, tmpdir))
3656 return false;
3657
3658 report_or_die("Path '%s' is not a subdirectory of MYSQLTEST_VARDIR '%s'"
3659 "or MYSQL_TMP_DIR '%s'",
3660 path, vardir, tmpdir);
3661 return true;
3662}
3663
3664
3665/*
3666 SYNOPSIS
3667 set_wild_chars
3668 set true to set * etc. as wild char, false to reset
3669
3670 DESCRIPTION
3671 Auxiliary function to set "our" wild chars before calling wild_compare
3672 This is needed because the default values are changed to SQL syntax
3673 in mysqltest_embedded.
3674*/
3675
3676void set_wild_chars (my_bool set)
3677{
3678 static char old_many= 0, old_one, old_prefix;
3679
3680 if (set)
3681 {
3682 if (wild_many == '*') return; // No need
3683 old_many= wild_many;
3684 old_one= wild_one;
3685 old_prefix= wild_prefix;
3686 wild_many= '*';
3687 wild_one= '?';
3688 wild_prefix= 0;
3689 }
3690 else
3691 {
3692 if (! old_many) return; // Was not set
3693 wild_many= old_many;
3694 wild_one= old_one;
3695 wild_prefix= old_prefix;
3696 }
3697}
3698
3699
3700/*
3701 SYNOPSIS
3702 do_remove_file
3703 command called command
3704
3705 DESCRIPTION
3706 remove_file <file_name>
3707 Remove the file <file_name>
3708*/
3709
3710void do_remove_file(struct st_command *command)
3711{
3712 int error;
3713 static DYNAMIC_STRING ds_filename;
3714 const struct command_arg rm_args[] = {
3715 { "filename", ARG_STRING, TRUE, &ds_filename, "File to delete" }
3716 };
3717 DBUG_ENTER("do_remove_file");
3718
3719 check_command_args(command, command->first_argument,
3720 rm_args, sizeof(rm_args)/sizeof(struct command_arg),
3721 ' ');
3722
3723 if (bad_path(ds_filename.str))
3724 DBUG_VOID_RETURN;
3725
3726 DBUG_PRINT("info", ("removing file: %s", ds_filename.str));
3727 error= my_delete(ds_filename.str, MYF(disable_warnings ? 0 : MY_WME)) != 0;
3728 handle_command_error(command, error, my_errno);
3729 dynstr_free(&ds_filename);
3730 DBUG_VOID_RETURN;
3731}
3732
3733
3734/*
3735 SYNOPSIS
3736 do_remove_files_wildcard
3737 command called command
3738
3739 DESCRIPTION
3740 remove_files_wildcard <directory> [<file_name_pattern>]
3741 Remove the files in <directory> optionally matching <file_name_pattern>
3742*/
3743
3744void do_remove_files_wildcard(struct st_command *command)
3745{
3746 int error= 0, sys_errno= 0;
3747 uint i;
3748 size_t directory_length;
3749 MY_DIR *dir_info;
3750 FILEINFO *file;
3751 char dir_separator[2];
3752 static DYNAMIC_STRING ds_directory;
3753 static DYNAMIC_STRING ds_wild;
3754 static DYNAMIC_STRING ds_file_to_remove;
3755 char dirname[FN_REFLEN];
3756
3757 const struct command_arg rm_args[] = {
3758 { "directory", ARG_STRING, TRUE, &ds_directory,
3759 "Directory containing files to delete" },
3760 { "filename", ARG_STRING, FALSE, &ds_wild, "File pattern to delete" }
3761 };
3762 DBUG_ENTER("do_remove_files_wildcard");
3763
3764 check_command_args(command, command->first_argument,
3765 rm_args, sizeof(rm_args)/sizeof(struct command_arg),
3766 ' ');
3767 fn_format(dirname, ds_directory.str, "", "", MY_UNPACK_FILENAME);
3768
3769 if (bad_path(ds_directory.str))
3770 DBUG_VOID_RETURN;
3771
3772 DBUG_PRINT("info", ("listing directory: %s", dirname));
3773 if (!(dir_info= my_dir(dirname, MYF(MY_DONT_SORT | MY_WANT_STAT | MY_WME))))
3774 {
3775 error= 1;
3776 sys_errno= my_errno;
3777 goto end;
3778 }
3779 init_dynamic_string(&ds_file_to_remove, dirname, 1024, 1024);
3780 dir_separator[0]= FN_LIBCHAR;
3781 dynstr_append_mem(&ds_file_to_remove, dir_separator, 1);
3782 directory_length= ds_file_to_remove.length;
3783
3784 /* Set default wild chars for wild_compare, is changed in embedded mode */
3785 set_wild_chars(1);
3786
3787 for (i= 0; i < (uint) dir_info->number_of_files; i++)
3788 {
3789 file= dir_info->dir_entry + i;
3790 /* Remove only regular files, i.e. no directories etc. */
3791 /* if (!MY_S_ISREG(file->mystat->st_mode)) */
3792 /* MY_S_ISREG does not work here on Windows, just skip directories */
3793 if (MY_S_ISDIR(file->mystat->st_mode))
3794 continue;
3795 if (ds_wild.length &&
3796 wild_compare(file->name, ds_wild.str, 0))
3797 continue;
3798 ds_file_to_remove.length= directory_length;
3799 dynstr_append(&ds_file_to_remove, file->name);
3800 DBUG_PRINT("info", ("removing file: %s", ds_file_to_remove.str));
3801 if ((error= (my_delete(ds_file_to_remove.str, MYF(MY_WME)) != 0)))
3802 sys_errno= my_errno;
3803 if (error)
3804 break;
3805 }
3806 set_wild_chars(0);
3807 my_dirend(dir_info);
3808
3809end:
3810 handle_command_error(command, error, sys_errno);
3811 dynstr_free(&ds_directory);
3812 dynstr_free(&ds_wild);
3813 dynstr_free(&ds_file_to_remove);
3814 DBUG_VOID_RETURN;
3815}
3816
3817
3818/*
3819 SYNOPSIS
3820 do_copy_file
3821 command command handle
3822
3823 DESCRIPTION
3824 copy_file <from_file> <to_file>
3825 Copy <from_file> to <to_file>
3826
3827 NOTE! Will fail if <to_file> exists
3828*/
3829
3830void do_copy_file(struct st_command *command)
3831{
3832 int error;
3833 static DYNAMIC_STRING ds_from_file;
3834 static DYNAMIC_STRING ds_to_file;
3835 const struct command_arg copy_file_args[] = {
3836 { "from_file", ARG_STRING, TRUE, &ds_from_file, "Filename to copy from" },
3837 { "to_file", ARG_STRING, TRUE, &ds_to_file, "Filename to copy to" }
3838 };
3839 DBUG_ENTER("do_copy_file");
3840
3841 check_command_args(command, command->first_argument,
3842 copy_file_args,
3843 sizeof(copy_file_args)/sizeof(struct command_arg),
3844 ' ');
3845
3846 if (bad_path(ds_to_file.str))
3847 DBUG_VOID_RETURN;
3848
3849 DBUG_PRINT("info", ("Copy %s to %s", ds_from_file.str, ds_to_file.str));
3850 /* MY_HOLD_ORIGINAL_MODES prevents attempts to chown the file */
3851 error= (my_copy(ds_from_file.str, ds_to_file.str,
3852 MYF(MY_DONT_OVERWRITE_FILE | MY_WME | MY_HOLD_ORIGINAL_MODES)) != 0);
3853 handle_command_error(command, error, my_errno);
3854 dynstr_free(&ds_from_file);
3855 dynstr_free(&ds_to_file);
3856 DBUG_VOID_RETURN;
3857}
3858
3859
3860/*
3861 SYNOPSIS
3862 do_move_file
3863 command command handle
3864
3865 DESCRIPTION
3866 move_file <from_file> <to_file>
3867 Move <from_file> to <to_file>
3868*/
3869
3870void do_move_file(struct st_command *command)
3871{
3872 int error;
3873 static DYNAMIC_STRING ds_from_file;
3874 static DYNAMIC_STRING ds_to_file;
3875 const struct command_arg move_file_args[] = {
3876 { "from_file", ARG_STRING, TRUE, &ds_from_file, "Filename to move from" },
3877 { "to_file", ARG_STRING, TRUE, &ds_to_file, "Filename to move to" }
3878 };
3879 DBUG_ENTER("do_move_file");
3880
3881 check_command_args(command, command->first_argument,
3882 move_file_args,
3883 sizeof(move_file_args)/sizeof(struct command_arg),
3884 ' ');
3885
3886 if (bad_path(ds_to_file.str))
3887 DBUG_VOID_RETURN;
3888
3889 DBUG_PRINT("info", ("Move %s to %s", ds_from_file.str, ds_to_file.str));
3890 error= (my_rename(ds_from_file.str, ds_to_file.str,
3891 MYF(disable_warnings ? 0 : MY_WME)) != 0);
3892 handle_command_error(command, error, my_errno);
3893 dynstr_free(&ds_from_file);
3894 dynstr_free(&ds_to_file);
3895 DBUG_VOID_RETURN;
3896}
3897
3898
3899/*
3900 SYNOPSIS
3901 do_chmod_file
3902 command command handle
3903
3904 DESCRIPTION
3905 chmod <octal> <file_name>
3906 Change file permission of <file_name>
3907
3908*/
3909
3910void do_chmod_file(struct st_command *command)
3911{
3912 long mode= 0;
3913 int err_code;
3914 static DYNAMIC_STRING ds_mode;
3915 static DYNAMIC_STRING ds_file;
3916 const struct command_arg chmod_file_args[] = {
3917 { "mode", ARG_STRING, TRUE, &ds_mode, "Mode of file(octal) ex. 0660"},
3918 { "filename", ARG_STRING, TRUE, &ds_file, "Filename of file to modify" }
3919 };
3920 DBUG_ENTER("do_chmod_file");
3921
3922 check_command_args(command, command->first_argument,
3923 chmod_file_args,
3924 sizeof(chmod_file_args)/sizeof(struct command_arg),
3925 ' ');
3926
3927 if (bad_path(ds_file.str))
3928 DBUG_VOID_RETURN;
3929
3930 /* Parse what mode to set */
3931 if (ds_mode.length != 4 ||
3932 str2int(ds_mode.str, 8, 0, INT_MAX, &mode) == NullS)
3933 die("You must write a 4 digit octal number for mode");
3934
3935 DBUG_PRINT("info", ("chmod %o %s", (uint)mode, ds_file.str));
3936 err_code= chmod(ds_file.str, mode);
3937 if (err_code < 0)
3938 err_code= 1;
3939 handle_command_error(command, err_code, errno);
3940 dynstr_free(&ds_mode);
3941 dynstr_free(&ds_file);
3942 DBUG_VOID_RETURN;
3943}
3944
3945
3946/*
3947 SYNOPSIS
3948 do_file_exists
3949 command called command
3950
3951 DESCRIPTION
3952 fiile_exist <file_name>
3953 Check if file <file_name> exists
3954*/
3955
3956void do_file_exist(struct st_command *command)
3957{
3958 int error;
3959 static DYNAMIC_STRING ds_filename;
3960 const struct command_arg file_exist_args[] = {
3961 { "filename", ARG_STRING, TRUE, &ds_filename, "File to check if it exist" }
3962 };
3963 DBUG_ENTER("do_file_exist");
3964
3965 check_command_args(command, command->first_argument,
3966 file_exist_args,
3967 sizeof(file_exist_args)/sizeof(struct command_arg),
3968 ' ');
3969
3970 DBUG_PRINT("info", ("Checking for existence of file: %s", ds_filename.str));
3971 error= (access(ds_filename.str, F_OK) != 0);
3972 handle_command_error(command, error, errno);
3973 dynstr_free(&ds_filename);
3974 DBUG_VOID_RETURN;
3975}
3976
3977
3978/*
3979 SYNOPSIS
3980 do_mkdir
3981 command called command
3982
3983 DESCRIPTION
3984 mkdir <dir_name>
3985 Create the directory <dir_name>
3986*/
3987
3988void do_mkdir(struct st_command *command)
3989{
3990 int error;
3991 static DYNAMIC_STRING ds_dirname;
3992 const struct command_arg mkdir_args[] = {
3993 {"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to create"}
3994 };
3995 DBUG_ENTER("do_mkdir");
3996
3997 check_command_args(command, command->first_argument,
3998 mkdir_args, sizeof(mkdir_args)/sizeof(struct command_arg),
3999 ' ');
4000
4001 if (bad_path(ds_dirname.str))
4002 DBUG_VOID_RETURN;
4003
4004 DBUG_PRINT("info", ("creating directory: %s", ds_dirname.str));
4005 error= my_mkdir(ds_dirname.str, 0777, MYF(MY_WME)) != 0;
4006 handle_command_error(command, error, my_errno);
4007 dynstr_free(&ds_dirname);
4008 DBUG_VOID_RETURN;
4009}
4010
4011
4012/*
4013 Remove directory recursively.
4014*/
4015static int rmtree(const char *dir)
4016{
4017 char path[FN_REFLEN];
4018 char sep[]={ FN_LIBCHAR, 0 };
4019 int err=0;
4020
4021 MY_DIR *dir_info= my_dir(dir, MYF(MY_DONT_SORT | MY_WANT_STAT));
4022 if (!dir_info)
4023 return 1;
4024
4025 for (uint i= 0; i < dir_info->number_of_files; i++)
4026 {
4027 FILEINFO *file= dir_info->dir_entry + i;
4028 /* Skip "." and ".." */
4029 if (!strcmp(file->name, ".") || !strcmp(file->name, ".."))
4030 continue;
4031
4032 strxnmov(path, sizeof(path), dir, sep, file->name, NULL);
4033
4034 if (!MY_S_ISDIR(file->mystat->st_mode))
4035 {
4036 err= my_delete(path, 0);
4037#ifdef _WIN32
4038 /*
4039 On Windows, check and possible reset readonly attribute.
4040 my_delete(), or DeleteFile does not remove theses files.
4041 */
4042 if (err)
4043 {
4044 DWORD attr= GetFileAttributes(path);
4045 if (attr != INVALID_FILE_ATTRIBUTES &&
4046 (attr & FILE_ATTRIBUTE_READONLY))
4047 {
4048 SetFileAttributes(path, attr &~ FILE_ATTRIBUTE_READONLY);
4049 err= my_delete(path, 0);
4050 }
4051 }
4052#endif
4053 }
4054 else
4055 err= rmtree(path);
4056
4057 if(err)
4058 break;
4059 }
4060
4061 my_dirend(dir_info);
4062
4063 if (!err)
4064 err= rmdir(dir);
4065
4066 return err;
4067}
4068
4069
4070/*
4071 SYNOPSIS
4072 do_rmdir
4073 command called command
4074
4075 DESCRIPTION
4076 rmdir <dir_name>
4077 Remove the directory tree
4078*/
4079
4080void do_rmdir(struct st_command *command)
4081{
4082 static DYNAMIC_STRING ds_dirname;
4083 const struct command_arg rmdir_args[] = {
4084 { "dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to remove" }
4085 };
4086 DBUG_ENTER("do_rmdir");
4087
4088 check_command_args(command, command->first_argument,
4089 rmdir_args, sizeof(rmdir_args)/sizeof(struct command_arg),
4090 ' ');
4091
4092 if (bad_path(ds_dirname.str))
4093 DBUG_VOID_RETURN;
4094
4095 DBUG_PRINT("info", ("removing directory: %s", ds_dirname.str));
4096 if (rmtree(ds_dirname.str))
4097 handle_command_error(command, 1, errno);
4098
4099 dynstr_free(&ds_dirname);
4100 DBUG_VOID_RETURN;
4101}
4102
4103
4104/*
4105 SYNOPSIS
4106 get_list_files
4107 ds output
4108 ds_dirname dir to list
4109 ds_wild wild-card file pattern (can be empty)
4110
4111 DESCRIPTION
4112 list all entries in directory (matching ds_wild if given)
4113*/
4114
4115static int get_list_files(DYNAMIC_STRING *ds, const DYNAMIC_STRING *ds_dirname,
4116 const DYNAMIC_STRING *ds_wild)
4117{
4118 uint i;
4119 MY_DIR *dir_info;
4120 FILEINFO *file;
4121 DBUG_ENTER("get_list_files");
4122
4123 DBUG_PRINT("info", ("listing directory: %s", ds_dirname->str));
4124 if (!(dir_info= my_dir(ds_dirname->str, MYF(MY_WANT_SORT))))
4125 DBUG_RETURN(1);
4126 set_wild_chars(1);
4127 for (i= 0; i < (uint) dir_info->number_of_files; i++)
4128 {
4129 file= dir_info->dir_entry + i;
4130 if (ds_wild && ds_wild->length &&
4131 wild_compare(file->name, ds_wild->str, 0))
4132 continue;
4133 replace_dynstr_append(ds, file->name);
4134 dynstr_append(ds, "\n");
4135 }
4136 set_wild_chars(0);
4137 my_dirend(dir_info);
4138 DBUG_RETURN(0);
4139}
4140
4141
4142/*
4143 SYNOPSIS
4144 do_list_files
4145 command called command
4146
4147 DESCRIPTION
4148 list_files <dir_name> [<file_name>]
4149 List files and directories in directory <dir_name> (like `ls`)
4150 [Matching <file_name>, where wild-cards are allowed]
4151*/
4152
4153static void do_list_files(struct st_command *command)
4154{
4155 int error;
4156 static DYNAMIC_STRING ds_dirname;
4157 static DYNAMIC_STRING ds_wild;
4158 const struct command_arg list_files_args[] = {
4159 {"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to list"},
4160 {"file", ARG_STRING, FALSE, &ds_wild, "Filename (incl. wildcard)"}
4161 };
4162 DBUG_ENTER("do_list_files");
4163 command->used_replace= 1;
4164
4165 check_command_args(command, command->first_argument,
4166 list_files_args,
4167 sizeof(list_files_args)/sizeof(struct command_arg), ' ');
4168
4169 error= get_list_files(&ds_res, &ds_dirname, &ds_wild);
4170 handle_command_error(command, error, my_errno);
4171 dynstr_free(&ds_dirname);
4172 dynstr_free(&ds_wild);
4173 DBUG_VOID_RETURN;
4174}
4175
4176
4177/*
4178 SYNOPSIS
4179 do_list_files_write_file_command
4180 command called command
4181 append append file, or create new
4182
4183 DESCRIPTION
4184 list_files_{write|append}_file <filename> <dir_name> [<match_file>]
4185 List files and directories in directory <dir_name> (like `ls`)
4186 [Matching <match_file>, where wild-cards are allowed]
4187
4188 Note: File will be truncated if exists and append is not true.
4189*/
4190
4191static void do_list_files_write_file_command(struct st_command *command,
4192 my_bool append)
4193{
4194 int error;
4195 static DYNAMIC_STRING ds_content;
4196 static DYNAMIC_STRING ds_filename;
4197 static DYNAMIC_STRING ds_dirname;
4198 static DYNAMIC_STRING ds_wild;
4199 const struct command_arg list_files_args[] = {
4200 {"filename", ARG_STRING, TRUE, &ds_filename, "Filename for write"},
4201 {"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to list"},
4202 {"file", ARG_STRING, FALSE, &ds_wild, "Filename (incl. wildcard)"}
4203 };
4204 DBUG_ENTER("do_list_files_write_file");
4205 command->used_replace= 1;
4206
4207 check_command_args(command, command->first_argument,
4208 list_files_args,
4209 sizeof(list_files_args)/sizeof(struct command_arg), ' ');
4210
4211 if (bad_path(ds_filename.str))
4212 DBUG_VOID_RETURN;
4213
4214 init_dynamic_string(&ds_content, "", 1024, 1024);
4215 error= get_list_files(&ds_content, &ds_dirname, &ds_wild);
4216 handle_command_error(command, error, my_errno);
4217 str_to_file2(ds_filename.str, ds_content.str, ds_content.length, append);
4218 dynstr_free(&ds_content);
4219 dynstr_free(&ds_filename);
4220 dynstr_free(&ds_dirname);
4221 dynstr_free(&ds_wild);
4222 DBUG_VOID_RETURN;
4223}
4224
4225
4226/*
4227 Read characters from line buffer or file. This is needed to allow
4228 my_ungetc() to buffer MAX_DELIMITER_LENGTH characters for a file
4229
4230 NOTE:
4231 This works as long as one doesn't change files (with 'source file_name')
4232 when there is things pushed into the buffer. This should however not
4233 happen for any tests in the test suite.
4234*/
4235
4236int my_getc(FILE *file)
4237{
4238 if (line_buffer_pos == line_buffer)
4239 return fgetc(file);
4240 return *--line_buffer_pos;
4241}
4242
4243
4244void my_ungetc(int c)
4245{
4246 *line_buffer_pos++= (char) c;
4247}
4248
4249
4250void read_until_delimiter(DYNAMIC_STRING *ds,
4251 DYNAMIC_STRING *ds_delimiter)
4252{
4253 char c;
4254 DBUG_ENTER("read_until_delimiter");
4255 DBUG_PRINT("enter", ("delimiter: %s, length: %u",
4256 ds_delimiter->str, (uint) ds_delimiter->length));
4257
4258 if (ds_delimiter->length > MAX_DELIMITER_LENGTH)
4259 die("Max delimiter length(%d) exceeded", MAX_DELIMITER_LENGTH);
4260
4261 /* Read from file until delimiter is found */
4262 while (1)
4263 {
4264 c= my_getc(cur_file->file);
4265 if (c == '\r')
4266 c= my_getc(cur_file->file);
4267 if (c == '\n')
4268 {
4269 cur_file->lineno++;
4270
4271 /* Skip newline from the same line as the command */
4272 if (start_lineno == (cur_file->lineno - 1))
4273 continue;
4274 }
4275 else if (start_lineno == cur_file->lineno)
4276 {
4277 /*
4278 No characters except \n are allowed on
4279 the same line as the command
4280 */
4281 report_or_die("Trailing characters found after command");
4282 }
4283
4284 if (feof(cur_file->file))
4285 report_or_die("End of file encountered before '%s' delimiter was found",
4286 ds_delimiter->str);
4287
4288 if (match_delimiter(c, ds_delimiter->str, ds_delimiter->length))
4289 {
4290 DBUG_PRINT("exit", ("Found delimiter '%s'", ds_delimiter->str));
4291 break;
4292 }
4293 dynstr_append_mem(ds, (const char*)&c, 1);
4294 }
4295 DBUG_PRINT("exit", ("ds: %s", ds->str));
4296 DBUG_VOID_RETURN;
4297}
4298
4299
4300void do_write_file_command(struct st_command *command, my_bool append)
4301{
4302 static DYNAMIC_STRING ds_content;
4303 static DYNAMIC_STRING ds_filename;
4304 static DYNAMIC_STRING ds_delimiter;
4305 const struct command_arg write_file_args[] = {
4306 { "filename", ARG_STRING, TRUE, &ds_filename, "File to write to" },
4307 { "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" }
4308 };
4309 DBUG_ENTER("do_write_file");
4310
4311 check_command_args(command,
4312 command->first_argument,
4313 write_file_args,
4314 sizeof(write_file_args)/sizeof(struct command_arg),
4315 ' ');
4316
4317 if (bad_path(ds_filename.str))
4318 DBUG_VOID_RETURN;
4319
4320 if (!append && access(ds_filename.str, F_OK) == 0)
4321 {
4322 /* The file should not be overwritten */
4323 die("File already exist: '%s'", ds_filename.str);
4324 }
4325
4326 ds_content= command->content;
4327 /* If it hasn't been done already by a loop iteration, fill it in */
4328 if (! ds_content.str)
4329 {
4330 /* If no delimiter was provided, use EOF */
4331 if (ds_delimiter.length == 0)
4332 dynstr_set(&ds_delimiter, "EOF");
4333
4334 init_dynamic_string(&ds_content, "", 1024, 1024);
4335 read_until_delimiter(&ds_content, &ds_delimiter);
4336 command->content= ds_content;
4337 }
4338 /* This function could be called even if "false", so check before printing */
4339 if (cur_block->ok)
4340 {
4341 DBUG_PRINT("info", ("Writing to file: %s", ds_filename.str));
4342 str_to_file2(ds_filename.str, ds_content.str, ds_content.length, append);
4343 }
4344 dynstr_free(&ds_filename);
4345 dynstr_free(&ds_delimiter);
4346 DBUG_VOID_RETURN;
4347}
4348
4349
4350/*
4351 SYNOPSIS
4352 do_write_file
4353 command called command
4354
4355 DESCRIPTION
4356 write_file <file_name> [<delimiter>];
4357 <what to write line 1>
4358 <...>
4359 < what to write line n>
4360 EOF
4361
4362 --write_file <file_name>;
4363 <what to write line 1>
4364 <...>
4365 < what to write line n>
4366 EOF
4367
4368 Write everything between the "write_file" command and 'delimiter'
4369 to "file_name"
4370
4371 NOTE! Will fail if <file_name> exists
4372
4373 Default <delimiter> is EOF
4374
4375*/
4376
4377void do_write_file(struct st_command *command)
4378{
4379 do_write_file_command(command, FALSE);
4380}
4381
4382
4383/*
4384 SYNOPSIS
4385 do_append_file
4386 command called command
4387
4388 DESCRIPTION
4389 append_file <file_name> [<delimiter>];
4390 <what to write line 1>
4391 <...>
4392 < what to write line n>
4393 EOF
4394
4395 --append_file <file_name>;
4396 <what to write line 1>
4397 <...>
4398 < what to write line n>
4399 EOF
4400
4401 Append everything between the "append_file" command
4402 and 'delimiter' to "file_name"
4403
4404 Default <delimiter> is EOF
4405
4406*/
4407
4408void do_append_file(struct st_command *command)
4409{
4410 do_write_file_command(command, TRUE);
4411}
4412
4413
4414/*
4415 SYNOPSIS
4416 do_cat_file
4417 command called command
4418
4419 DESCRIPTION
4420 cat_file <file_name>;
4421
4422 Print the given file to result log
4423
4424*/
4425
4426void do_cat_file(struct st_command *command)
4427{
4428 int error;
4429 static DYNAMIC_STRING ds_filename;
4430 const struct command_arg cat_file_args[] = {
4431 { "filename", ARG_STRING, TRUE, &ds_filename, "File to read from" }
4432 };
4433 DBUG_ENTER("do_cat_file");
4434
4435 check_command_args(command,
4436 command->first_argument,
4437 cat_file_args,
4438 sizeof(cat_file_args)/sizeof(struct command_arg),
4439 ' ');
4440
4441 DBUG_PRINT("info", ("Reading from, file: %s", ds_filename.str));
4442
4443 error= cat_file(&ds_res, ds_filename.str);
4444 handle_command_error(command, error, my_errno);
4445 dynstr_free(&ds_filename);
4446 DBUG_VOID_RETURN;
4447}
4448
4449
4450/*
4451 SYNOPSIS
4452 do_diff_files
4453 command called command
4454
4455 DESCRIPTION
4456 diff_files <file1> <file2>;
4457
4458 Fails if the two files differ.
4459
4460*/
4461
4462void do_diff_files(struct st_command *command)
4463{
4464 int error= 0;
4465 static DYNAMIC_STRING ds_filename;
4466 static DYNAMIC_STRING ds_filename2;
4467 const struct command_arg diff_file_args[] = {
4468 { "file1", ARG_STRING, TRUE, &ds_filename, "First file to diff" },
4469 { "file2", ARG_STRING, TRUE, &ds_filename2, "Second file to diff" }
4470 };
4471 DBUG_ENTER("do_diff_files");
4472
4473 check_command_args(command,
4474 command->first_argument,
4475 diff_file_args,
4476 sizeof(diff_file_args)/sizeof(struct command_arg),
4477 ' ');
4478
4479 if (access(ds_filename.str, F_OK) != 0)
4480 die("command \"diff_files\" failed, file '%s' does not exist",
4481 ds_filename.str);
4482
4483 if (access(ds_filename2.str, F_OK) != 0)
4484 die("command \"diff_files\" failed, file '%s' does not exist",
4485 ds_filename2.str);
4486
4487 if ((error= compare_files(ds_filename.str, ds_filename2.str)) &&
4488 match_expected_error(command, error, NULL) < 0)
4489 {
4490 /*
4491 Compare of the two files failed, append them to output
4492 so the failure can be analyzed, but only if it was not
4493 expected to fail.
4494 */
4495 show_diff(&ds_res, ds_filename.str, ds_filename2.str);
4496 log_file.write(&ds_res);
4497 log_file.flush();
4498 dynstr_set(&ds_res, 0);
4499 }
4500
4501 dynstr_free(&ds_filename);
4502 dynstr_free(&ds_filename2);
4503 handle_command_error(command, error, -1);
4504 DBUG_VOID_RETURN;
4505}
4506
4507
4508struct st_connection * find_connection_by_name(const char *name)
4509{
4510 struct st_connection *con;
4511 for (con= connections; con < next_con; con++)
4512 {
4513 if (!strcmp(con->name, name))
4514 {
4515 return con;
4516 }
4517 }
4518 return 0; /* Connection not found */
4519}
4520
4521
4522/*
4523 SYNOPSIS
4524 do_send_quit
4525 command called command
4526
4527 DESCRIPTION
4528 Sends a simple quit command to the server for the named connection.
4529
4530*/
4531
4532void do_send_quit(struct st_command *command)
4533{
4534 char *p= command->first_argument, *name;
4535 struct st_connection *con;
4536
4537 DBUG_ENTER("do_send_quit");
4538 DBUG_PRINT("enter",("name: '%s'",p));
4539
4540 if (!*p)
4541 die("Missing connection name in send_quit");
4542 name= p;
4543 while (*p && !my_isspace(charset_info,*p))
4544 p++;
4545
4546 if (*p)
4547 *p++= 0;
4548 command->last_argument= p;
4549
4550 if (!(con= find_connection_by_name(name)))
4551 die("connection '%s' not found in connection pool", name);
4552
4553 simple_command(con->mysql,COM_QUIT,0,0,1);
4554
4555 DBUG_VOID_RETURN;
4556}
4557
4558
4559/*
4560 SYNOPSIS
4561 do_change_user
4562 command called command
4563
4564 DESCRIPTION
4565 change_user [<user>], [<passwd>], [<db>]
4566 <user> - user to change to
4567 <passwd> - user password
4568 <db> - default database
4569
4570 Changes the user and causes the database specified by db to become
4571 the default (current) database for the the current connection.
4572
4573*/
4574
4575void do_change_user(struct st_command *command)
4576{
4577 MYSQL *mysql = cur_con->mysql;
4578 /* static keyword to make the NetWare compiler happy. */
4579 static DYNAMIC_STRING ds_user, ds_passwd, ds_db;
4580 const struct command_arg change_user_args[] = {
4581 { "user", ARG_STRING, FALSE, &ds_user, "User to connect as" },
4582 { "password", ARG_STRING, FALSE, &ds_passwd, "Password used when connecting" },
4583 { "database", ARG_STRING, FALSE, &ds_db, "Database to select after connect" },
4584 };
4585
4586 DBUG_ENTER("do_change_user");
4587
4588 check_command_args(command, command->first_argument,
4589 change_user_args,
4590 sizeof(change_user_args)/sizeof(struct command_arg),
4591 ',');
4592
4593 if (cur_con->stmt)
4594 {
4595 mysql_stmt_close(cur_con->stmt);
4596 cur_con->stmt= NULL;
4597 }
4598
4599 if (!ds_user.length)
4600 {
4601 dynstr_set(&ds_user, mysql->user);
4602
4603 if (!ds_passwd.length)
4604 dynstr_set(&ds_passwd, mysql->passwd);
4605
4606 if (!ds_db.length)
4607 dynstr_set(&ds_db, mysql->db);
4608 }
4609
4610 DBUG_PRINT("info",("connection: '%s' user: '%s' password: '%s' database: '%s'",
4611 cur_con->name, ds_user.str, ds_passwd.str, ds_db.str));
4612
4613 if (mysql_change_user(mysql, ds_user.str, ds_passwd.str, ds_db.str))
4614 handle_error(command, mysql_errno(mysql), mysql_error(mysql),
4615 mysql_sqlstate(mysql), &ds_res);
4616 else
4617 handle_no_error(command);
4618
4619 dynstr_free(&ds_user);
4620 dynstr_free(&ds_passwd);
4621 dynstr_free(&ds_db);
4622
4623 DBUG_VOID_RETURN;
4624}
4625
4626
4627/*
4628 SYNOPSIS
4629 do_perl
4630 command command handle
4631
4632 DESCRIPTION
4633 perl [<delimiter>];
4634 <perlscript line 1>
4635 <...>
4636 <perlscript line n>
4637 EOF
4638
4639 Execute everything after "perl" until <delimiter> as perl.
4640 Useful for doing more advanced things
4641 but still being able to execute it on all platforms.
4642
4643 Default <delimiter> is EOF
4644*/
4645
4646void do_perl(struct st_command *command)
4647{
4648 int error;
4649 File fd;
4650 FILE *res_file;
4651 char buf[FN_REFLEN];
4652 char temp_file_path[FN_REFLEN];
4653 static DYNAMIC_STRING ds_script;
4654 static DYNAMIC_STRING ds_delimiter;
4655 const struct command_arg perl_args[] = {
4656 { "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" }
4657 };
4658 DBUG_ENTER("do_perl");
4659
4660 check_command_args(command,
4661 command->first_argument,
4662 perl_args,
4663 sizeof(perl_args)/sizeof(struct command_arg),
4664 ' ');
4665
4666 ds_script= command->content;
4667 /* If it hasn't been done already by a loop iteration, fill it in */
4668 if (! ds_script.str)
4669 {
4670 /* If no delimiter was provided, use EOF */
4671 if (ds_delimiter.length == 0)
4672 dynstr_set(&ds_delimiter, "EOF");
4673
4674 init_dynamic_string(&ds_script, "", 1024, 1024);
4675 read_until_delimiter(&ds_script, &ds_delimiter);
4676 command->content= ds_script;
4677 }
4678
4679 /* This function could be called even if "false", so check before doing */
4680 if (cur_block->ok)
4681 {
4682 DBUG_PRINT("info", ("Executing perl: %s", ds_script.str));
4683
4684 /* Create temporary file name */
4685 if ((fd= create_temp_file(temp_file_path, getenv("MYSQLTEST_VARDIR"),
4686 "tmp", O_SHARE, MYF(MY_WME))) < 0)
4687 die("Failed to create temporary file for perl command");
4688 my_close(fd, MYF(0));
4689
4690 str_to_file(temp_file_path, ds_script.str, ds_script.length);
4691
4692 /* Format the "perl <filename>" command */
4693 my_snprintf(buf, sizeof(buf), "perl %s", temp_file_path);
4694
4695 if (!(res_file= my_popen(buf, "r")))
4696 {
4697 if (command->abort_on_error)
4698 die("popen(\"%s\", \"r\") failed", buf);
4699 dynstr_free(&ds_delimiter);
4700 DBUG_VOID_RETURN;
4701 }
4702
4703 int len;
4704 while (my_fgets(buf, sizeof(buf), res_file,&len))
4705 {
4706 if (disable_result_log)
4707 {
4708 buf[len - 1] = 0;
4709 DBUG_PRINT("exec_result", ("%s", buf));
4710 }
4711 else
4712 {
4713 replace_dynstr_append_mem(&ds_res, buf, len);
4714 }
4715 }
4716 error= pclose(res_file);
4717
4718 /* Remove the temporary file, but keep it if perl failed */
4719 if (!error)
4720 my_delete(temp_file_path, MYF(MY_WME));
4721
4722 /* Check for error code that indicates perl could not be started */
4723 int exstat= WEXITSTATUS(error);
4724#ifdef _WIN32
4725 if (exstat == 1)
4726 /* Text must begin 'perl not found' as mtr looks for it */
4727 abort_not_supported_test("perl not found in path or did not start");
4728#else
4729 if (exstat == 127)
4730 abort_not_supported_test("perl not found in path");
4731#endif
4732 else
4733 handle_command_error(command, exstat, my_errno);
4734 }
4735 dynstr_free(&ds_delimiter);
4736 DBUG_VOID_RETURN;
4737}
4738
4739
4740/*
4741 Print the content between echo and <delimiter> to result file.
4742 Evaluate all variables in the string before printing, allow
4743 for variable names to be escaped using \
4744
4745 SYNOPSIS
4746 do_echo()
4747 command called command
4748
4749 DESCRIPTION
4750 echo text
4751 Print the text after echo until end of command to result file
4752
4753 echo $<var_name>
4754 Print the content of the variable <var_name> to result file
4755
4756 echo Some text $<var_name>
4757 Print "Some text" plus the content of the variable <var_name> to
4758 result file
4759
4760 echo Some text \$<var_name>
4761 Print "Some text" plus $<var_name> to result file
4762*/
4763
4764int do_echo(struct st_command *command)
4765{
4766 DYNAMIC_STRING ds_echo;
4767 DBUG_ENTER("do_echo");
4768
4769 init_dynamic_string(&ds_echo, "", command->query_len, 256);
4770 do_eval(&ds_echo, command->first_argument, command->end, FALSE);
4771 dynstr_append_mem(&ds_res, ds_echo.str, ds_echo.length);
4772 dynstr_append_mem(&ds_res, "\n", 1);
4773 dynstr_free(&ds_echo);
4774 command->last_argument= command->end;
4775 DBUG_RETURN(0);
4776}
4777
4778
4779void do_wait_for_slave_to_stop(struct st_command *c __attribute__((unused)))
4780{
4781 static int SLAVE_POLL_INTERVAL= 300000;
4782 MYSQL* mysql = cur_con->mysql;
4783 for (;;)
4784 {
4785 MYSQL_RES *UNINIT_VAR(res);
4786 MYSQL_ROW row;
4787 int done;
4788
4789 if (mysql_query(mysql,"show status like 'Slave_running'") ||
4790 !(res=mysql_store_result(mysql)))
4791 die("Query failed while probing slave for stop: %s",
4792 mysql_error(mysql));
4793 if (!(row=mysql_fetch_row(res)) || !row[1])
4794 {
4795 mysql_free_result(res);
4796 die("Strange result from query while probing slave for stop");
4797 }
4798 done = !strcmp(row[1],"OFF");
4799 mysql_free_result(res);
4800 if (done)
4801 break;
4802 my_sleep(SLAVE_POLL_INTERVAL);
4803 }
4804 return;
4805}
4806
4807
4808void do_sync_with_master2(struct st_command *command, long offset,
4809 const char *connection_name)
4810{
4811 MYSQL_RES *res;
4812 MYSQL_ROW row;
4813 MYSQL *mysql= cur_con->mysql;
4814 char query_buf[FN_REFLEN+128];
4815 int timeout= opt_wait_for_pos_timeout;
4816
4817 if (!master_pos.file[0])
4818 die("Calling 'sync_with_master' without calling 'save_master_pos'");
4819
4820 sprintf(query_buf, "select master_pos_wait('%s', %ld, %d, '%s')",
4821 master_pos.file, master_pos.pos + offset, timeout,
4822 connection_name);
4823
4824 if (mysql_query(mysql, query_buf))
4825 die("failed in '%s': %d: %s", query_buf, mysql_errno(mysql),
4826 mysql_error(mysql));
4827
4828 if (!(res= mysql_store_result(mysql)))
4829 die("mysql_store_result() returned NULL for '%s'", query_buf);
4830 if (!(row= mysql_fetch_row(res)))
4831 {
4832 mysql_free_result(res);
4833 die("empty result in %s", query_buf);
4834 }
4835
4836 int result= -99;
4837 const char* result_str= row[0];
4838 if (result_str)
4839 result= atoi(result_str);
4840
4841 mysql_free_result(res);
4842
4843 if (!result_str || result < 0)
4844 {
4845 /* master_pos_wait returned NULL or < 0 */
4846 show_query(mysql, "SHOW MASTER STATUS");
4847 show_query(mysql, "SHOW SLAVE STATUS");
4848 show_query(mysql, "SHOW PROCESSLIST");
4849 fprintf(stderr, "analyze: sync_with_master\n");
4850
4851 if (!result_str)
4852 {
4853 /*
4854 master_pos_wait returned NULL. This indicates that
4855 slave SQL thread is not started, the slave's master
4856 information is not initialized, the arguments are
4857 incorrect, or an error has occurred
4858 */
4859 die("%.*s failed: '%s' returned NULL " \
4860 "indicating slave SQL thread failure",
4861 command->first_word_len, command->query, query_buf);
4862
4863 }
4864
4865 if (result == -1)
4866 die("%.*s failed: '%s' returned -1 " \
4867 "indicating timeout after %d seconds",
4868 command->first_word_len, command->query, query_buf, timeout);
4869 else
4870 die("%.*s failed: '%s' returned unknown result :%d",
4871 command->first_word_len, command->query, query_buf, result);
4872 }
4873
4874 return;
4875}
4876
4877void do_sync_with_master(struct st_command *command)
4878{
4879 long offset= 0;
4880 char *p= command->first_argument;
4881 const char *offset_start= p;
4882 char *start, *buff= 0;
4883 start= const_cast<char*>("");
4884
4885 if (*offset_start)
4886 {
4887 for (; my_isdigit(charset_info, *p); p++)
4888 offset = offset * 10 + *p - '0';
4889
4890 if (*p && !my_isspace(charset_info, *p) && *p != ',')
4891 die("Invalid integer argument \"%s\"", offset_start);
4892
4893 while (*p && my_isspace(charset_info, *p))
4894 p++;
4895 if (*p == ',')
4896 {
4897 p++;
4898 while (*p && my_isspace(charset_info, *p))
4899 p++;
4900 start= buff= (char*)my_malloc(strlen(p)+1,MYF(MY_WME | MY_FAE));
4901 get_string(&buff, &p, command);
4902 }
4903 command->last_argument= p;
4904 }
4905 do_sync_with_master2(command, offset, start);
4906 if (buff)
4907 my_free(start);
4908 return;
4909}
4910
4911
4912int do_save_master_pos()
4913{
4914 MYSQL_RES *res;
4915 MYSQL_ROW row;
4916 MYSQL *mysql = cur_con->mysql;
4917 const char *query;
4918 DBUG_ENTER("do_save_master_pos");
4919
4920 if (mysql_query(mysql, query= "show master status"))
4921 die("failed in 'show master status': %d %s",
4922 mysql_errno(mysql), mysql_error(mysql));
4923
4924 if (!(res = mysql_store_result(mysql)))
4925 die("mysql_store_result() retuned NULL for '%s'", query);
4926 if (!(row = mysql_fetch_row(res)))
4927 die("empty result in show master status");
4928 strnmov(master_pos.file, row[0], sizeof(master_pos.file)-1);
4929 master_pos.pos = strtoul(row[1], (char**) 0, 10);
4930 mysql_free_result(res);
4931 DBUG_RETURN(0);
4932}
4933
4934
4935/*
4936 Assign the variable <var_name> with <var_val>
4937
4938 SYNOPSIS
4939 do_let()
4940 query called command
4941
4942 DESCRIPTION
4943 let $<var_name>=<var_val><delimiter>
4944
4945 <var_name> - is the string string found between the $ and =
4946 <var_val> - is the content between the = and <delimiter>, it may span
4947 multiple line and contain any characters except <delimiter>
4948 <delimiter> - is a string containing of one or more chars, default is ;
4949
4950 RETURN VALUES
4951 Program will die if error detected
4952*/
4953
4954void do_let(struct st_command *command)
4955{
4956 char *p= command->first_argument;
4957 char *var_name, *var_name_end;
4958 DYNAMIC_STRING let_rhs_expr;
4959 DBUG_ENTER("do_let");
4960
4961 init_dynamic_string(&let_rhs_expr, "", 512, 2048);
4962
4963 /* Find <var_name> */
4964 if (!*p)
4965 die("Missing arguments to let");
4966 var_name= p;
4967 while (*p && (*p != '=') && !my_isspace(charset_info,*p))
4968 p++;
4969 var_name_end= p;
4970 if (var_name == var_name_end ||
4971 (var_name+1 == var_name_end && *var_name == '$'))
4972 die("Missing variable name in let");
4973 while (my_isspace(charset_info,*p))
4974 p++;
4975 if (*p++ != '=')
4976 die("Missing assignment operator in let");
4977
4978 /* Find start of <var_val> */
4979 while (*p && my_isspace(charset_info,*p))
4980 p++;
4981
4982 do_eval(&let_rhs_expr, p, command->end, FALSE);
4983
4984 command->last_argument= command->end;
4985 /* Assign var_val to var_name */
4986 var_set(var_name, var_name_end, let_rhs_expr.str,
4987 (let_rhs_expr.str + let_rhs_expr.length));
4988 dynstr_free(&let_rhs_expr);
4989 revert_properties();
4990 DBUG_VOID_RETURN;
4991}
4992
4993
4994/*
4995 Sleep the number of specified seconds
4996
4997 SYNOPSIS
4998 do_sleep()
4999 q called command
5000 real_sleep use the value from opt_sleep as number of seconds to sleep
5001 if real_sleep is false
5002
5003 DESCRIPTION
5004 sleep <seconds>
5005 real_sleep <seconds>
5006
5007 The difference between the sleep and real_sleep commands is that sleep
5008 uses the delay from the --sleep command-line option if there is one.
5009 (If the --sleep option is not given, the sleep command uses the delay
5010 specified by its argument.) The real_sleep command always uses the
5011 delay specified by its argument. The logic is that sometimes delays are
5012 cpu-dependent, and --sleep can be used to set this delay. real_sleep is
5013 used for cpu-independent delays.
5014*/
5015
5016int do_sleep(struct st_command *command, my_bool real_sleep)
5017{
5018 int error= 0;
5019 char *sleep_start, *sleep_end;
5020 double sleep_val;
5021 char *p;
5022 static DYNAMIC_STRING ds_sleep;
5023 const struct command_arg sleep_args[] = {
5024 { "sleep_delay", ARG_STRING, TRUE, &ds_sleep, "Number of seconds to sleep." }
5025 };
5026 check_command_args(command, command->first_argument, sleep_args,
5027 sizeof(sleep_args)/sizeof(struct command_arg),
5028 ' ');
5029
5030 p= ds_sleep.str;
5031 sleep_end= ds_sleep.str + ds_sleep.length;
5032 while (my_isspace(charset_info, *p))
5033 p++;
5034 if (!*p)
5035 die("Missing argument to %.*s", command->first_word_len,
5036 command->query);
5037 sleep_start= p;
5038 /* Check that arg starts with a digit, not handled by my_strtod */
5039 if (!my_isdigit(charset_info, *sleep_start))
5040 die("Invalid argument to %.*s \"%s\"", command->first_word_len,
5041 command->query, sleep_start);
5042 sleep_val= my_strtod(sleep_start, &sleep_end, &error);
5043 check_eol_junk_line(sleep_end);
5044 if (error)
5045 die("Invalid argument to %.*s \"%s\"", command->first_word_len,
5046 command->query, command->first_argument);
5047 dynstr_free(&ds_sleep);
5048
5049 /* Fixed sleep time selected by --sleep option */
5050 if (opt_sleep >= 0 && !real_sleep)
5051 sleep_val= opt_sleep;
5052
5053 DBUG_PRINT("info", ("sleep_val: %f", sleep_val));
5054 if (sleep_val)
5055 my_sleep((ulong) (sleep_val * 1000000L));
5056 return 0;
5057}
5058
5059
5060void do_get_file_name(struct st_command *command,
5061 char* dest, uint dest_max_len)
5062{
5063 char *p= command->first_argument, *name;
5064 if (!*p)
5065 die("Missing file name argument");
5066 name= p;
5067 while (*p && !my_isspace(charset_info,*p))
5068 p++;
5069 if (*p)
5070 *p++= 0;
5071 command->last_argument= p;
5072 strmake(dest, name, dest_max_len - 1);
5073}
5074
5075
5076void do_set_charset(struct st_command *command)
5077{
5078 char *charset_name= command->first_argument;
5079 char *p;
5080
5081 if (!charset_name || !*charset_name)
5082 die("Missing charset name in 'character_set'");
5083 /* Remove end space */
5084 p= charset_name;
5085 while (*p && !my_isspace(charset_info,*p))
5086 p++;
5087 if(*p)
5088 *p++= 0;
5089 command->last_argument= p;
5090 charset_info= get_charset_by_csname(charset_name,MY_CS_PRIMARY,MYF(MY_WME));
5091 if (!charset_info)
5092 abort_not_supported_test("Test requires charset '%s'", charset_name);
5093}
5094
5095
5096/*
5097 Run query and return one field in the result set from the
5098 first row and <column>
5099*/
5100
5101int query_get_string(MYSQL* mysql, const char* query,
5102 int column, DYNAMIC_STRING* ds)
5103{
5104 MYSQL_RES *res= NULL;
5105 MYSQL_ROW row;
5106
5107 if (mysql_query(mysql, query))
5108 {
5109 report_or_die("'%s' failed: %d %s", query,
5110 mysql_errno(mysql), mysql_error(mysql));
5111 return 1;
5112 }
5113 if ((res= mysql_store_result(mysql)) == NULL)
5114 {
5115 report_or_die("Failed to store result: %d %s",
5116 mysql_errno(mysql), mysql_error(mysql));
5117 return 1;
5118 }
5119
5120 if ((row= mysql_fetch_row(res)) == NULL)
5121 {
5122 mysql_free_result(res);
5123 return 1;
5124 }
5125 init_dynamic_string(ds, (row[column] ? row[column] : "NULL"), ~0, 32);
5126 mysql_free_result(res);
5127 return 0;
5128}
5129
5130
5131static int my_kill(int pid, int sig)
5132{
5133 DBUG_PRINT("info", ("Killing server, pid: %d", pid));
5134#ifdef _WIN32
5135#define SIGKILL 9 /* ignored anyway, see below */
5136 HANDLE proc;
5137 if ((proc= OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, pid)) == NULL)
5138 return -1;
5139 if (sig == 0)
5140 {
5141 DWORD wait_result= WaitForSingleObject(proc, 0);
5142 CloseHandle(proc);
5143 return wait_result == WAIT_OBJECT_0?-1:0;
5144 }
5145 (void)TerminateProcess(proc, 201);
5146 CloseHandle(proc);
5147 return 1;
5148#else
5149 return kill(pid, sig);
5150#endif
5151}
5152
5153
5154
5155/*
5156 Shutdown the server of current connection and
5157 make sure it goes away within <timeout> seconds
5158
5159 NOTE! Currently only works with local server
5160
5161 SYNOPSIS
5162 do_shutdown_server()
5163 command called command
5164
5165 DESCRIPTION
5166 shutdown_server [<timeout>]
5167
5168*/
5169
5170
5171static int wait_until_dead(int pid, int timeout)
5172{
5173 DBUG_ENTER("wait_until_dead");
5174 /* Check that server dies */
5175 while (timeout--)
5176 {
5177 if (my_kill(pid, 0) < 0)
5178 {
5179 DBUG_PRINT("info", ("Process %d does not exist anymore", pid));
5180 DBUG_RETURN(0);
5181 }
5182 DBUG_PRINT("info", ("Sleeping, timeout: %d", timeout));
5183 /* Sleep one second */
5184 my_sleep(1000000L);
5185 }
5186 DBUG_RETURN(1); // Did not die
5187}
5188
5189
5190void do_shutdown_server(struct st_command *command)
5191{
5192 long timeout= opt_wait_for_pos_timeout ? opt_wait_for_pos_timeout / 5 : 300;
5193 int pid;
5194 DYNAMIC_STRING ds_pidfile_name;
5195 MYSQL* mysql = cur_con->mysql;
5196 static DYNAMIC_STRING ds_timeout;
5197 const struct command_arg shutdown_args[] = {
5198 {"timeout", ARG_STRING, FALSE, &ds_timeout, "Timeout before killing server"}
5199 };
5200 DBUG_ENTER("do_shutdown_server");
5201
5202 check_command_args(command, command->first_argument, shutdown_args,
5203 sizeof(shutdown_args)/sizeof(struct command_arg),
5204 ' ');
5205
5206 if (ds_timeout.length)
5207 {
5208 char* endptr;
5209 timeout= strtol(ds_timeout.str, &endptr, 10);
5210 if (*endptr != '\0')
5211 die("Illegal argument for timeout: '%s'", ds_timeout.str);
5212 }
5213 dynstr_free(&ds_timeout);
5214
5215 /* Get the servers pid_file name and use it to read pid */
5216 if (query_get_string(mysql, "SHOW VARIABLES LIKE 'pid_file'", 1,
5217 &ds_pidfile_name))
5218 die("Failed to get pid_file from server");
5219
5220 /* Read the pid from the file */
5221 {
5222 int fd;
5223 char buff[32];
5224
5225 if ((fd= my_open(ds_pidfile_name.str, O_RDONLY, MYF(0))) < 0)
5226 die("Failed to open file '%s'", ds_pidfile_name.str);
5227 dynstr_free(&ds_pidfile_name);
5228
5229 if (my_read(fd, (uchar*)&buff, sizeof(buff), MYF(0)) <= 0){
5230 my_close(fd, MYF(0));
5231 die("pid file was empty");
5232 }
5233 my_close(fd, MYF(0));
5234
5235 pid= atoi(buff);
5236 if (pid == 0)
5237 die("Pidfile didn't contain a valid number");
5238 }
5239 DBUG_PRINT("info", ("Got pid %d", pid));
5240
5241 /*
5242 If timeout == 0, it means we should kill the server hard, without
5243 any shutdown or core (SIGKILL)
5244
5245 If timeout is given, then we do things in the following order:
5246 - mysql_shutdown()
5247 - If server is not dead within timeout
5248 - kill SIGABRT (to get a core)
5249 - If server is not dead within new timeout
5250 - kill SIGKILL
5251 */
5252
5253 if (timeout && mysql_shutdown(mysql, SHUTDOWN_DEFAULT))
5254 die("mysql_shutdown failed");
5255
5256 if (!timeout || wait_until_dead(pid, timeout))
5257 {
5258 if (timeout)
5259 (void) my_kill(pid, SIGABRT);
5260 /* Give server a few seconds to die in all cases */
5261 if (!timeout || wait_until_dead(pid, timeout < 5 ? 5 : timeout))
5262 {
5263 (void) my_kill(pid, SIGKILL);
5264 }
5265 }
5266 DBUG_VOID_RETURN;
5267}
5268
5269
5270/* List of error names to error codes */
5271typedef struct
5272{
5273 const char *name;
5274 uint code;
5275 const char *text;
5276} st_error;
5277
5278static st_error global_error_names[] =
5279{
5280 { "<No error>", ~0U, "" },
5281#include <mysqld_ername.h>
5282 { 0, 0, 0 }
5283};
5284
5285#include <my_base.h>
5286static st_error handler_error_names[] =
5287{
5288 { "<No error>", UINT_MAX, "" },
5289#include <handler_ername.h>
5290 { 0, 0, 0 }
5291};
5292
5293uint get_errcode_from_name(const char *error_name, const char *error_end,
5294 st_error *e)
5295{
5296 DBUG_ENTER("get_errcode_from_name");
5297 DBUG_PRINT("enter", ("error_name: %s", error_name));
5298
5299 /* Loop through the array of known error names */
5300 for (; e->name; e++)
5301 {
5302 /*
5303 If we get a match, we need to check the length of the name we
5304 matched against in case it was longer than what we are checking
5305 (as in ER_WRONG_VALUE vs. ER_WRONG_VALUE_COUNT).
5306 */
5307 if (!strncmp(error_name, e->name, (int) (error_end - error_name)) &&
5308 (uint) strlen(e->name) == (uint) (error_end - error_name))
5309 {
5310 DBUG_RETURN(e->code);
5311 }
5312 }
5313 DBUG_RETURN(0);
5314}
5315
5316
5317uint get_errcode_from_name(const char *error_name, const char *error_end)
5318{
5319 uint tmp;
5320 if ((tmp= get_errcode_from_name(error_name, error_end,
5321 global_error_names)))
5322 return tmp;
5323 if ((tmp= get_errcode_from_name(error_name, error_end,
5324 handler_error_names)))
5325 return tmp;
5326 die("Unknown SQL error name '%s'", error_name);
5327 return 0; // Keep compiler happy
5328}
5329
5330const char *unknown_error= "<Unknown>";
5331
5332const char *get_errname_from_code (uint error_code, st_error *e)
5333{
5334 DBUG_ENTER("get_errname_from_code");
5335 DBUG_PRINT("enter", ("error_code: %d", error_code));
5336
5337 if (! error_code)
5338 {
5339 DBUG_RETURN("");
5340 }
5341 for (; e->name; e++)
5342 {
5343 if (e->code == error_code)
5344 {
5345 DBUG_RETURN(e->name);
5346 }
5347 }
5348 /* Apparently, errors without known names may occur */
5349 DBUG_RETURN(unknown_error);
5350}
5351
5352const char *get_errname_from_code(uint error_code)
5353{
5354 const char *name;
5355 if ((name= get_errname_from_code(error_code, global_error_names)) !=
5356 unknown_error)
5357 return name;
5358 return get_errname_from_code(error_code, handler_error_names);
5359}
5360
5361void do_get_errcodes(struct st_command *command)
5362{
5363 struct st_match_err *to= saved_expected_errors.err;
5364 DBUG_ENTER("do_get_errcodes");
5365
5366 if (!*command->first_argument)
5367 die("Missing argument(s) to 'error'");
5368
5369 /* TODO: Potentially, there is a possibility of variables
5370 being expanded twice, e.g.
5371
5372 let $errcodes = 1,\$a;
5373 let $a = 1051;
5374 error $errcodes;
5375 DROP TABLE unknown_table;
5376 ...
5377 Got one of the listed errors
5378
5379 But since it requires manual escaping, it does not seem
5380 particularly dangerous or error-prone.
5381 */
5382 DYNAMIC_STRING ds;
5383 init_dynamic_string(&ds, 0, command->query_len + 64, 256);
5384 do_eval(&ds, command->first_argument, command->end, !is_windows);
5385 char *p= ds.str;
5386
5387 uint count= 0;
5388 char *next;
5389
5390 do
5391 {
5392 char *end;
5393
5394 /* Skip leading spaces */
5395 while (*p && *p == ' ')
5396 p++;
5397
5398 /* Find end */
5399 end= p;
5400 while (*end && *end != ',' && *end != ' ')
5401 end++;
5402
5403 next=end;
5404
5405 /* code to handle variables passed to mysqltest */
5406 if( *p == '$')
5407 {
5408 const char* fin;
5409 VAR *var = var_get(p,&fin,0,0);
5410 p=var->str_val;
5411 end=p+var->str_val_len;
5412 }
5413
5414 if (*p == 'S')
5415 {
5416 char *to_ptr= to->code.sqlstate;
5417
5418 /*
5419 SQLSTATE string
5420 - Must be SQLSTATE_LENGTH long
5421 - May contain only digits[0-9] and _uppercase_ letters
5422 */
5423 p++; /* Step past the S */
5424 if ((end - p) != SQLSTATE_LENGTH)
5425 die("The sqlstate must be exactly %d chars long", SQLSTATE_LENGTH);
5426
5427 /* Check sqlstate string validity */
5428 while (*p && p < end)
5429 {
5430 if (my_isdigit(charset_info, *p) || my_isupper(charset_info, *p))
5431 *to_ptr++= *p++;
5432 else
5433 die("The sqlstate may only consist of digits[0-9] " \
5434 "and _uppercase_ letters");
5435 }
5436
5437 *to_ptr= 0;
5438 to->type= ERR_SQLSTATE;
5439 DBUG_PRINT("info", ("ERR_SQLSTATE: %s", to->code.sqlstate));
5440 }
5441 else if (*p == 's')
5442 {
5443 die("The sqlstate definition must start with an uppercase S");
5444 }
5445 else if (*p == 'E' || *p == 'W' || *p == 'H')
5446 {
5447 /* Error name string */
5448
5449 DBUG_PRINT("info", ("Error name: %s", p));
5450 to->code.errnum= get_errcode_from_name(p, end);
5451 to->type= ERR_ERRNO;
5452 DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum));
5453 }
5454 else if (*p == 'e' || *p == 'w' || *p == 'h')
5455 {
5456 die("The error name definition must start with an uppercase E or W or H");
5457 }
5458 else
5459 {
5460 long val;
5461 char *start= p;
5462 /* Check that the string passed to str2int only contain digits */
5463 while (*p && p != end)
5464 {
5465 if (!my_isdigit(charset_info, *p))
5466 die("Invalid argument to error: '%s' - " \
5467 "the errno may only consist of digits[0-9]",
5468 command->first_argument);
5469 p++;
5470 }
5471
5472 /* Convert the sting to int */
5473 if (!str2int(start, 10, (long) INT_MIN, (long) INT_MAX, &val))
5474 die("Invalid argument to error: '%s'", command->first_argument);
5475
5476 to->code.errnum= (uint) val;
5477 to->type= ERR_ERRNO;
5478 DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum));
5479 }
5480 to++;
5481 count++;
5482
5483 if (count >= (sizeof(saved_expected_errors.err) /
5484 sizeof(struct st_match_err)))
5485 die("Too many errorcodes specified");
5486
5487 /* Set pointer to the end of the last error code */
5488 p= next;
5489
5490 /* Find next ',' */
5491 while (*p && *p != ',')
5492 p++;
5493
5494 if (*p)
5495 p++; /* Step past ',' */
5496
5497 } while (*p);
5498
5499 command->last_argument= command->first_argument;
5500 while (*command->last_argument)
5501 command->last_argument++;
5502
5503 to->type= ERR_EMPTY; /* End of data */
5504
5505 DBUG_PRINT("info", ("Expected errors: %d", count));
5506 saved_expected_errors.count= count;
5507 dynstr_free(&ds);
5508 DBUG_VOID_RETURN;
5509}
5510
5511
5512/*
5513 Get a string; Return ptr to end of string
5514 Strings may be surrounded by " or '
5515
5516 If string is a '$variable', return the value of the variable.
5517*/
5518
5519static char *get_string(char **to_ptr, char **from_ptr,
5520 struct st_command *command)
5521{
5522 char c, sep;
5523 char *to= *to_ptr, *from= *from_ptr, *start=to;
5524 DBUG_ENTER("get_string");
5525
5526 /* Find separator */
5527 if (*from == '"' || *from == '\'')
5528 sep= *from++;
5529 else
5530 sep=' '; /* Separated with space */
5531
5532 for ( ; (c=*from) ; from++)
5533 {
5534 if (c == '\\' && from[1])
5535 { /* Escaped character */
5536 /* We can't translate \0 -> ASCII 0 as replace can't handle ASCII 0 */
5537 switch (*++from) {
5538 case 'n':
5539 *to++= '\n';
5540 break;
5541 case 't':
5542 *to++= '\t';
5543 break;
5544 case 'r':
5545 *to++ = '\r';
5546 break;
5547 case 'b':
5548 *to++ = '\b';
5549 break;
5550 case 'Z': /* ^Z must be escaped on Win32 */
5551 *to++='\032';
5552 break;
5553 default:
5554 *to++ = *from;
5555 break;
5556 }
5557 }
5558 else if (c == sep)
5559 {
5560 if (c == ' ' || c != *++from)
5561 break; /* Found end of string */
5562 *to++=c; /* Copy duplicated separator */
5563 }
5564 else
5565 *to++=c;
5566 }
5567 if (*from != ' ' && *from)
5568 die("Wrong string argument in %s", command->query);
5569
5570 while (my_isspace(charset_info,*from)) /* Point to next string */
5571 from++;
5572
5573 *to =0; /* End of string marker */
5574 *to_ptr= to+1; /* Store pointer to end */
5575 *from_ptr= from;
5576
5577 /* Check if this was a variable */
5578 if (*start == '$')
5579 {
5580 const char *end= to;
5581 VAR *var=var_get(start, &end, 0, 1);
5582 if (var && to == (char*) end+1)
5583 {
5584 DBUG_PRINT("info",("var: '%s' -> '%s'", start, var->str_val));
5585 DBUG_RETURN(var->str_val); /* return found variable value */
5586 }
5587 }
5588 DBUG_RETURN(start);
5589}
5590
5591
5592
5593
5594/**
5595 Change the current connection to the given st_connection, and update
5596 $mysql_get_server_version and $CURRENT_CONNECTION accordingly.
5597*/
5598void set_current_connection(struct st_connection *con)
5599{
5600 cur_con= con;
5601 /* Update $mysql_get_server_version to that of current connection */
5602 var_set_int("$mysql_get_server_version",
5603 mysql_get_server_version(con->mysql));
5604 /* Update $CURRENT_CONNECTION to the name of the current connection */
5605 var_set_string("$CURRENT_CONNECTION", con->name);
5606}
5607
5608
5609void select_connection_name(const char *name)
5610{
5611 DBUG_ENTER("select_connection_name");
5612 DBUG_PRINT("enter",("name: '%s'", name));
5613 st_connection *con= find_connection_by_name(name);
5614
5615 if (!con)
5616 die("connection '%s' not found in connection pool", name);
5617
5618 set_current_connection(con);
5619
5620 /* Connection logging if enabled */
5621 if (!disable_connect_log && !disable_query_log)
5622 {
5623 DYNAMIC_STRING *ds= &ds_res;
5624
5625 dynstr_append_mem(ds, "connection ", 11);
5626 replace_dynstr_append(ds, name);
5627 dynstr_append_mem(ds, ";\n", 2);
5628 }
5629
5630 DBUG_VOID_RETURN;
5631}
5632
5633
5634void select_connection(struct st_command *command)
5635{
5636 DBUG_ENTER("select_connection");
5637 static DYNAMIC_STRING ds_connection;
5638 const struct command_arg connection_args[] = {
5639 { "connection_name", ARG_STRING, TRUE, &ds_connection, "Name of the connection that we switch to." }
5640 };
5641 check_command_args(command, command->first_argument, connection_args,
5642 sizeof(connection_args)/sizeof(struct command_arg),
5643 ' ');
5644
5645 DBUG_PRINT("info", ("changing connection: %s", ds_connection.str));
5646 select_connection_name(ds_connection.str);
5647 dynstr_free(&ds_connection);
5648 DBUG_VOID_RETURN;
5649}
5650
5651
5652void do_close_connection(struct st_command *command)
5653{
5654 DBUG_ENTER("do_close_connection");
5655
5656 struct st_connection *con;
5657 static DYNAMIC_STRING ds_connection;
5658 const struct command_arg close_connection_args[] = {
5659 { "connection_name", ARG_STRING, TRUE, &ds_connection,
5660 "Name of the connection to close." }
5661 };
5662 check_command_args(command, command->first_argument,
5663 close_connection_args,
5664 sizeof(close_connection_args)/sizeof(struct command_arg),
5665 ' ');
5666
5667 DBUG_PRINT("enter",("connection name: '%s'", ds_connection.str));
5668
5669 if (!(con= find_connection_by_name(ds_connection.str)))
5670 die("connection '%s' not found in connection pool", ds_connection.str);
5671
5672 DBUG_PRINT("info", ("Closing connection %s", con->name));
5673#ifndef EMBEDDED_LIBRARY
5674 if (command->type == Q_DIRTY_CLOSE)
5675 {
5676 mariadb_cancel(con->mysql);
5677 }
5678#endif /*!EMBEDDED_LIBRARY*/
5679 if (con->stmt)
5680 do_stmt_close(con);
5681 con->stmt= 0;
5682#ifdef EMBEDDED_LIBRARY
5683 /*
5684 As query could be still executed in a separate theread
5685 we need to check if the query's thread was finished and probably wait
5686 (embedded-server specific)
5687 */
5688 emb_close_connection(con);
5689#endif /*EMBEDDED_LIBRARY*/
5690
5691 mysql_close(con->mysql);
5692 con->mysql= 0;
5693
5694 if (con->util_mysql)
5695 mysql_close(con->util_mysql);
5696 con->util_mysql= 0;
5697 con->pending= FALSE;
5698
5699 my_free(con->name);
5700
5701 /*
5702 When the connection is closed set name to "-closed_connection-"
5703 to make it possible to reuse the connection name.
5704 */
5705 if (!(con->name = my_strdup("-closed_connection-", MYF(MY_WME))))
5706 die("Out of memory");
5707
5708 if (con == cur_con)
5709 {
5710 /* Current connection was closed */
5711 var_set_int("$mysql_get_server_version", 0xFFFFFFFF);
5712 var_set_string("$CURRENT_CONNECTION", con->name);
5713 }
5714
5715 /* Connection logging if enabled */
5716 if (!disable_connect_log && !disable_query_log)
5717 {
5718 DYNAMIC_STRING *ds= &ds_res;
5719
5720 dynstr_append_mem(ds, "disconnect ", 11);
5721 replace_dynstr_append(ds, ds_connection.str);
5722 dynstr_append_mem(ds, ";\n", 2);
5723 }
5724
5725 dynstr_free(&ds_connection);
5726 DBUG_VOID_RETURN;
5727}
5728
5729
5730/*
5731 Connect to a server doing several retries if needed.
5732
5733 SYNOPSIS
5734 safe_connect()
5735 con - connection structure to be used
5736 host, user, pass, - connection parameters
5737 db, port, sock
5738
5739 NOTE
5740
5741 Sometimes in a test the client starts before
5742 the server - to solve the problem, we try again
5743 after some sleep if connection fails the first
5744 time
5745
5746 This function will try to connect to the given server
5747 "opt_max_connect_retries" times and sleep "connection_retry_sleep"
5748 seconds between attempts before finally giving up.
5749 This helps in situation when the client starts
5750 before the server (which happens sometimes).
5751 It will only ignore connection errors during these retries.
5752
5753*/
5754
5755void safe_connect(MYSQL* mysql, const char *name, const char *host,
5756 const char *user, const char *pass, const char *db,
5757 int port, const char *sock)
5758{
5759 int failed_attempts= 0;
5760
5761 DBUG_ENTER("safe_connect");
5762
5763 verbose_msg("Connecting to server %s:%d (socket %s) as '%s'"
5764 ", connection '%s', attempt %d ...",
5765 host, port, sock, user, name, failed_attempts);
5766
5767 mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
5768 mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
5769 "program_name", "mysqltest");
5770 while(!mysql_real_connect(mysql, host,user, pass, db, port, sock,
5771 CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS))
5772 {
5773 /*
5774 Connect failed
5775
5776 Only allow retry if this was an error indicating the server
5777 could not be contacted. Error code differs depending
5778 on protocol/connection type
5779 */
5780
5781 if ((mysql_errno(mysql) == CR_CONN_HOST_ERROR ||
5782 mysql_errno(mysql) == CR_CONNECTION_ERROR) &&
5783 failed_attempts < opt_max_connect_retries)
5784 {
5785 verbose_msg("Connect attempt %d/%d failed: %d: %s", failed_attempts,
5786 opt_max_connect_retries, mysql_errno(mysql),
5787 mysql_error(mysql));
5788 my_sleep(connection_retry_sleep);
5789 }
5790 else
5791 {
5792 if (failed_attempts > 0)
5793 die("Could not open connection '%s' after %d attempts: %d %s", name,
5794 failed_attempts, mysql_errno(mysql), mysql_error(mysql));
5795 else
5796 die("Could not open connection '%s': %d %s", name,
5797 mysql_errno(mysql), mysql_error(mysql));
5798 }
5799 failed_attempts++;
5800 }
5801 verbose_msg("... Connected.");
5802 DBUG_VOID_RETURN;
5803}
5804
5805
5806/*
5807 Connect to a server and handle connection errors in case they occur.
5808
5809 SYNOPSIS
5810 connect_n_handle_errors()
5811 q - context of connect "query" (command)
5812 con - connection structure to be used
5813 host, user, pass, - connection parameters
5814 db, port, sock
5815
5816 DESCRIPTION
5817 This function will try to establish a connection to server and handle
5818 possible errors in the same manner as if "connect" was usual SQL-statement
5819 (If error is expected it will ignore it once it occurs and log the
5820 "statement" to the query log).
5821 Unlike safe_connect() it won't do several attempts.
5822
5823 RETURN VALUES
5824 1 - Connected
5825 0 - Not connected
5826
5827*/
5828
5829int connect_n_handle_errors(struct st_command *command,
5830 MYSQL* con, const char* host,
5831 const char* user, const char* pass,
5832 const char* db, int port, const char* sock)
5833{
5834 DYNAMIC_STRING *ds;
5835 int failed_attempts= 0;
5836
5837 ds= &ds_res;
5838
5839 /* Only log if an error is expected */
5840 if (command->expected_errors.count > 0 &&
5841 !disable_query_log)
5842 {
5843 /*
5844 Log the connect to result log
5845 */
5846 dynstr_append_mem(ds, "connect(", 8);
5847 replace_dynstr_append(ds, host);
5848 dynstr_append_mem(ds, ",", 1);
5849 replace_dynstr_append(ds, user);
5850 dynstr_append_mem(ds, ",", 1);
5851 replace_dynstr_append(ds, pass);
5852 dynstr_append_mem(ds, ",", 1);
5853 if (db)
5854 replace_dynstr_append(ds, db);
5855 dynstr_append_mem(ds, ",", 1);
5856 replace_dynstr_append_uint(ds, port);
5857 dynstr_append_mem(ds, ",", 1);
5858 if (sock)
5859 replace_dynstr_append(ds, sock);
5860 dynstr_append_mem(ds, ")", 1);
5861 dynstr_append_mem(ds, delimiter, delimiter_length);
5862 dynstr_append_mem(ds, "\n", 1);
5863 }
5864 /* Simlified logging if enabled */
5865 if (!disable_connect_log && !disable_query_log)
5866 {
5867 replace_dynstr_append(ds, command->query);
5868 dynstr_append_mem(ds, ";\n", 2);
5869 }
5870
5871 mysql_options(con, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
5872 mysql_options4(con, MYSQL_OPT_CONNECT_ATTR_ADD, "program_name", "mysqltest");
5873 while (!mysql_real_connect(con, host, user, pass, db, port, sock ? sock: 0,
5874 CLIENT_MULTI_STATEMENTS))
5875 {
5876 /*
5877 If we have used up all our connections check whether this
5878 is expected (by --error). If so, handle the error right away.
5879 Otherwise, give it some extra time to rule out race-conditions.
5880 If extra-time doesn't help, we have an unexpected error and
5881 must abort -- just proceeding to handle_error() when second
5882 and third chances are used up will handle that for us.
5883
5884 There are various user-limits of which only max_user_connections
5885 and max_connections_per_hour apply at connect time. For the
5886 the second to create a race in our logic, we'd need a limits
5887 test that runs without a FLUSH for longer than an hour, so we'll
5888 stay clear of trying to work out which exact user-limit was
5889 exceeded.
5890 */
5891
5892 if (((mysql_errno(con) == ER_TOO_MANY_USER_CONNECTIONS) ||
5893 (mysql_errno(con) == ER_USER_LIMIT_REACHED)) &&
5894 (failed_attempts++ < opt_max_connect_retries))
5895 {
5896 int i;
5897
5898 i= match_expected_error(command, mysql_errno(con), mysql_sqlstate(con));
5899
5900 if (i >= 0)
5901 goto do_handle_error; /* expected error, handle */
5902
5903 my_sleep(connection_retry_sleep); /* unexpected error, wait */
5904 continue; /* and give it 1 more chance */
5905 }
5906
5907do_handle_error:
5908 var_set_errno(mysql_errno(con));
5909 handle_error(command, mysql_errno(con), mysql_error(con),
5910 mysql_sqlstate(con), ds);
5911 return 0; /* Not connected */
5912 }
5913
5914 var_set_errno(0);
5915 handle_no_error(command);
5916 revert_properties();
5917 return 1; /* Connected */
5918}
5919
5920
5921/*
5922 Open a new connection to MySQL Server with the parameters
5923 specified. Make the new connection the current connection.
5924
5925 SYNOPSIS
5926 do_connect()
5927 q called command
5928
5929 DESCRIPTION
5930 connect(<name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]]);
5931 connect <name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]];
5932
5933 <name> - name of the new connection
5934 <host> - hostname of server
5935 <user> - user to connect as
5936 <pass> - password used when connecting
5937 <db> - initial db when connected
5938 <port> - server port
5939 <sock> - server socket
5940 <opts> - options to use for the connection
5941 * SSL - use SSL if available
5942 * COMPRESS - use compression if available
5943 * SHM - use shared memory if available
5944 * PIPE - use named pipe if available
5945
5946*/
5947
5948void do_connect(struct st_command *command)
5949{
5950 int con_port= opt_port;
5951 char *con_options;
5952 char *ssl_cipher __attribute__((unused))= 0;
5953 my_bool con_ssl= 0, con_compress= 0;
5954 my_bool con_pipe= 0;
5955 my_bool con_shm __attribute__ ((unused))= 0;
5956 int read_timeout= 0;
5957 int write_timeout= 0;
5958 int connect_timeout= 0;
5959 struct st_connection* con_slot;
5960
5961 static DYNAMIC_STRING ds_connection_name;
5962 static DYNAMIC_STRING ds_host;
5963 static DYNAMIC_STRING ds_user;
5964 static DYNAMIC_STRING ds_password;
5965 static DYNAMIC_STRING ds_database;
5966 static DYNAMIC_STRING ds_port;
5967 static DYNAMIC_STRING ds_sock;
5968 static DYNAMIC_STRING ds_options;
5969 static DYNAMIC_STRING ds_default_auth;
5970#ifdef HAVE_SMEM
5971 static DYNAMIC_STRING ds_shm;
5972#endif
5973 const struct command_arg connect_args[] = {
5974 { "connection name", ARG_STRING, TRUE, &ds_connection_name, "Name of the connection" },
5975 { "host", ARG_STRING, TRUE, &ds_host, "Host to connect to" },
5976 { "user", ARG_STRING, FALSE, &ds_user, "User to connect as" },
5977 { "passsword", ARG_STRING, FALSE, &ds_password, "Password used when connecting" },
5978 { "database", ARG_STRING, FALSE, &ds_database, "Database to select after connect" },
5979 { "port", ARG_STRING, FALSE, &ds_port, "Port to connect to" },
5980 { "socket", ARG_STRING, FALSE, &ds_sock, "Socket to connect with" },
5981 { "options", ARG_STRING, FALSE, &ds_options, "Options to use while connecting" },
5982 { "default_auth", ARG_STRING, FALSE, &ds_default_auth, "Default authentication to use" }
5983 };
5984
5985 DBUG_ENTER("do_connect");
5986 DBUG_PRINT("enter",("connect: %s", command->first_argument));
5987
5988 strip_parentheses(command);
5989 check_command_args(command, command->first_argument, connect_args,
5990 sizeof(connect_args)/sizeof(struct command_arg),
5991 ',');
5992
5993 /* Port */
5994 if (ds_port.length)
5995 {
5996 con_port= atoi(ds_port.str);
5997 if (con_port == 0)
5998 die("Illegal argument for port: '%s'", ds_port.str);
5999 }
6000
6001#ifdef HAVE_SMEM
6002 /* Shared memory */
6003 init_dynamic_string(&ds_shm, ds_sock.str, 0, 0);
6004#endif
6005
6006 /* Sock */
6007 if (ds_sock.length)
6008 {
6009 /*
6010 If the socket is specified just as a name without path
6011 append tmpdir in front
6012 */
6013 if (*ds_sock.str != FN_LIBCHAR)
6014 {
6015 char buff[FN_REFLEN];
6016 fn_format(buff, ds_sock.str, TMPDIR, "", 0);
6017 dynstr_set(&ds_sock, buff);
6018 }
6019 }
6020 else
6021 {
6022 /* No socket specified, use default */
6023 dynstr_set(&ds_sock, unix_sock);
6024 }
6025 DBUG_PRINT("info", ("socket: %s", ds_sock.str));
6026
6027
6028 /* Options */
6029 con_options= ds_options.str;
6030 while (*con_options)
6031 {
6032 size_t length;
6033 char *end;
6034 /* Step past any spaces in beginning of option*/
6035 while (*con_options && my_isspace(charset_info, *con_options))
6036 con_options++;
6037 /* Find end of this option */
6038 end= con_options;
6039 while (*end && !my_isspace(charset_info, *end))
6040 end++;
6041 length= (size_t) (end - con_options);
6042 if (length == 3 && !strncmp(con_options, "SSL", 3))
6043 con_ssl= 1;
6044 else if (!strncmp(con_options, "SSL-CIPHER=", 11))
6045 {
6046 con_ssl= 1;
6047 ssl_cipher=con_options + 11;
6048 }
6049 else if (length == 8 && !strncmp(con_options, "COMPRESS", 8))
6050 con_compress= 1;
6051 else if (length == 4 && !strncmp(con_options, "PIPE", 4))
6052 con_pipe= 1;
6053 else if (length == 3 && !strncmp(con_options, "SHM", 3))
6054 con_shm= 1;
6055 else if (strncasecmp(con_options, "read_timeout=",
6056 sizeof("read_timeout=")-1) == 0)
6057 {
6058 read_timeout= atoi(con_options + sizeof("read_timeout=")-1);
6059 }
6060 else if (strncasecmp(con_options, "write_timeout=",
6061 sizeof("write_timeout=")-1) == 0)
6062 {
6063 write_timeout= atoi(con_options + sizeof("write_timeout=")-1);
6064 }
6065 else if (strncasecmp(con_options, "connect_timeout=",
6066 sizeof("connect_timeout=")-1) == 0)
6067 {
6068 connect_timeout= atoi(con_options + sizeof("connect_timeout=")-1);
6069 }
6070 else
6071 die("Illegal option to connect: %.*s",
6072 (int) (end - con_options), con_options);
6073 /* Process next option */
6074 con_options= end;
6075 }
6076
6077 if (find_connection_by_name(ds_connection_name.str))
6078 die("Connection %s already exists", ds_connection_name.str);
6079
6080 if (next_con != connections_end)
6081 con_slot= next_con;
6082 else
6083 {
6084 if (!(con_slot= find_connection_by_name("-closed_connection-")))
6085 die("Connection limit exhausted, you can have max %d connections",
6086 opt_max_connections);
6087 my_free(con_slot->name);
6088 con_slot->name= 0;
6089 }
6090
6091 init_connection_thd(con_slot);
6092
6093 if (!(con_slot->mysql= mysql_init(0)))
6094 die("Failed on mysql_init()");
6095
6096 if (opt_connect_timeout)
6097 mysql_options(con_slot->mysql, MYSQL_OPT_CONNECT_TIMEOUT,
6098 (void *) &opt_connect_timeout);
6099#ifndef MY_CONTEXT_DISABLE
6100 if (mysql_options(con_slot->mysql, MYSQL_OPT_NONBLOCK, 0))
6101 die("Failed to initialise non-blocking API");
6102#endif
6103 if (opt_compress || con_compress)
6104 mysql_options(con_slot->mysql, MYSQL_OPT_COMPRESS, NullS);
6105 mysql_options(con_slot->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
6106 mysql_options(con_slot->mysql, MYSQL_SET_CHARSET_NAME,
6107 charset_info->csname);
6108 if (opt_charsets_dir)
6109 mysql_options(con_slot->mysql, MYSQL_SET_CHARSET_DIR,
6110 opt_charsets_dir);
6111#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
6112 if (opt_use_ssl)
6113 con_ssl= 1;
6114#endif
6115
6116 if (con_ssl)
6117 {
6118#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
6119 mysql_ssl_set(con_slot->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
6120 opt_ssl_capath, ssl_cipher ? ssl_cipher : opt_ssl_cipher);
6121 mysql_options(con_slot->mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl);
6122 mysql_options(con_slot->mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath);
6123#if MYSQL_VERSION_ID >= 50000
6124 /* Turn on ssl_verify_server_cert only if host is "localhost" */
6125 opt_ssl_verify_server_cert= !strcmp(ds_host.str, "localhost");
6126 mysql_options(con_slot->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
6127 &opt_ssl_verify_server_cert);
6128#endif
6129#endif
6130 }
6131
6132 if (con_pipe)
6133 {
6134#ifdef _WIN32
6135 opt_protocol= MYSQL_PROTOCOL_PIPE;
6136#endif
6137 }
6138
6139 if (opt_protocol)
6140 mysql_options(con_slot->mysql, MYSQL_OPT_PROTOCOL, (char*) &opt_protocol);
6141
6142 if (read_timeout)
6143 {
6144 mysql_options(con_slot->mysql, MYSQL_OPT_READ_TIMEOUT,
6145 (char*)&read_timeout);
6146 }
6147
6148 if (write_timeout)
6149 {
6150 mysql_options(con_slot->mysql, MYSQL_OPT_WRITE_TIMEOUT,
6151 (char*)&write_timeout);
6152 }
6153
6154 if (connect_timeout)
6155 {
6156 mysql_options(con_slot->mysql, MYSQL_OPT_CONNECT_TIMEOUT,
6157 (char*)&connect_timeout);
6158 }
6159
6160#ifdef HAVE_SMEM
6161 if (con_shm)
6162 {
6163 uint protocol= MYSQL_PROTOCOL_MEMORY;
6164 if (!ds_shm.length)
6165 die("Missing shared memory base name");
6166 mysql_options(con_slot->mysql, MYSQL_SHARED_MEMORY_BASE_NAME, ds_shm.str);
6167 mysql_options(con_slot->mysql, MYSQL_OPT_PROTOCOL, &protocol);
6168 }
6169 else if (shared_memory_base_name)
6170 {
6171 mysql_options(con_slot->mysql, MYSQL_SHARED_MEMORY_BASE_NAME,
6172 shared_memory_base_name);
6173 }
6174#endif
6175
6176 /* Use default db name */
6177 if (ds_database.length == 0)
6178 dynstr_set(&ds_database, opt_db);
6179
6180 if (opt_plugin_dir && *opt_plugin_dir)
6181 mysql_options(con_slot->mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
6182
6183 if (ds_default_auth.length)
6184 mysql_options(con_slot->mysql, MYSQL_DEFAULT_AUTH, ds_default_auth.str);
6185
6186 /* Special database to allow one to connect without a database name */
6187 if (ds_database.length && !strcmp(ds_database.str,"*NO-ONE*"))
6188 dynstr_set(&ds_database, "");
6189
6190 if (connect_n_handle_errors(command, con_slot->mysql,
6191 ds_host.str,ds_user.str,
6192 ds_password.str, ds_database.str,
6193 con_port, ds_sock.str))
6194 {
6195 DBUG_PRINT("info", ("Inserting connection %s in connection pool",
6196 ds_connection_name.str));
6197 if (!(con_slot->name= my_strdup(ds_connection_name.str, MYF(MY_WME))))
6198 die("Out of memory");
6199 con_slot->name_len= strlen(con_slot->name);
6200 set_current_connection(con_slot);
6201
6202 if (con_slot == next_con)
6203 next_con++; /* if we used the next_con slot, advance the pointer */
6204 }
6205
6206 dynstr_free(&ds_connection_name);
6207 dynstr_free(&ds_host);
6208 dynstr_free(&ds_user);
6209 dynstr_free(&ds_password);
6210 dynstr_free(&ds_database);
6211 dynstr_free(&ds_port);
6212 dynstr_free(&ds_sock);
6213 dynstr_free(&ds_options);
6214 dynstr_free(&ds_default_auth);
6215#ifdef HAVE_SMEM
6216 dynstr_free(&ds_shm);
6217#endif
6218 DBUG_VOID_RETURN;
6219}
6220
6221
6222int do_done(struct st_command *command)
6223{
6224 /* Check if empty block stack */
6225 if (cur_block == block_stack)
6226 {
6227 if (*command->query != '}')
6228 die("Stray 'end' command - end of block before beginning");
6229 die("Stray '}' - end of block before beginning");
6230 }
6231
6232 /* Test if inner block has been executed */
6233 if (cur_block->ok && cur_block->cmd == cmd_while)
6234 {
6235 /* Pop block from stack, re-execute outer block */
6236 cur_block--;
6237 parser.current_line = cur_block->line;
6238 }
6239 else
6240 {
6241 if (*cur_block->delim)
6242 {
6243 /* Restore "old" delimiter after false if block */
6244 strcpy (delimiter, cur_block->delim);
6245 delimiter_length= strlen(delimiter);
6246 }
6247 /* Pop block from stack, goto next line */
6248 cur_block--;
6249 parser.current_line++;
6250 }
6251 return 0;
6252}
6253
6254/* Operands available in if or while conditions */
6255
6256enum block_op {
6257 EQ_OP,
6258 NE_OP,
6259 GT_OP,
6260 GE_OP,
6261 LT_OP,
6262 LE_OP,
6263 ILLEG_OP
6264};
6265
6266
6267enum block_op find_operand(const char *start)
6268{
6269 char first= *start;
6270 char next= *(start+1);
6271
6272 if (first == '=' && next == '=')
6273 return EQ_OP;
6274 if (first == '!' && next == '=')
6275 return NE_OP;
6276 if (first == '>' && next == '=')
6277 return GE_OP;
6278 if (first == '>')
6279 return GT_OP;
6280 if (first == '<' && next == '=')
6281 return LE_OP;
6282 if (first == '<')
6283 return LT_OP;
6284
6285 return ILLEG_OP;
6286}
6287
6288
6289/*
6290 Process start of a "if" or "while" statement
6291
6292 SYNOPSIS
6293 do_block()
6294 cmd Type of block
6295 q called command
6296
6297 DESCRIPTION
6298 if ([!]<expr>)
6299 {
6300 <block statements>
6301 }
6302
6303 while ([!]<expr>)
6304 {
6305 <block statements>
6306 }
6307
6308 Evaluates the <expr> and if it evaluates to
6309 greater than zero executes the following code block.
6310 A '!' can be used before the <expr> to indicate it should
6311 be executed if it evaluates to zero.
6312
6313 <expr> can also be a simple comparison condition:
6314
6315 <variable> <op> <expr>
6316
6317 The left hand side must be a variable, the right hand side can be a
6318 variable, number, string or `query`. Operands are ==, !=, <, <=, >, >=.
6319 == and != can be used for strings, all can be used for numerical values.
6320*/
6321
6322void do_block(enum block_cmd cmd, struct st_command* command)
6323{
6324 char *p= command->first_argument;
6325 const char *expr_start, *expr_end;
6326 VAR v;
6327 const char *cmd_name= (cmd == cmd_while ? "while" : "if");
6328 my_bool not_expr= FALSE;
6329 DBUG_ENTER("do_block");
6330 DBUG_PRINT("enter", ("%s", cmd_name));
6331
6332 /* Check stack overflow */
6333 if (cur_block == block_stack_end)
6334 die("Nesting too deeply");
6335
6336 /* Set way to find outer block again, increase line counter */
6337 cur_block->line= parser.current_line++;
6338
6339 /* If this block is ignored */
6340 if (!cur_block->ok)
6341 {
6342 /* Inner block should be ignored too */
6343 cur_block++;
6344 cur_block->cmd= cmd;
6345 cur_block->ok= FALSE;
6346 cur_block->delim[0]= '\0';
6347 DBUG_VOID_RETURN;
6348 }
6349
6350 /* Parse and evaluate test expression */
6351 expr_start= strchr(p, '(');
6352 if (!expr_start++)
6353 die("missing '(' in %s", cmd_name);
6354
6355 while (my_isspace(charset_info, *expr_start))
6356 expr_start++;
6357
6358 /* Check for !<expr> */
6359 if (*expr_start == '!')
6360 {
6361 not_expr= TRUE;
6362 expr_start++; /* Step past the '!', then any whitespace */
6363 while (*expr_start && my_isspace(charset_info, *expr_start))
6364 expr_start++;
6365 }
6366 /* Find ending ')' */
6367 expr_end= strrchr(expr_start, ')');
6368 if (!expr_end)
6369 die("missing ')' in %s", cmd_name);
6370 p= (char*)expr_end+1;
6371
6372 while (*p && my_isspace(charset_info, *p))
6373 p++;
6374 if (*p && *p != '{')
6375 die("Missing '{' after %s. Found \"%s\"", cmd_name, p);
6376
6377 var_init(&v,0,0,0,0);
6378
6379 /* If expression starts with a variable, it may be a compare condition */
6380
6381 if (*expr_start == '$')
6382 {
6383 const char *curr_ptr= expr_end;
6384 eval_expr(&v, expr_start, &curr_ptr, true);
6385 while (my_isspace(charset_info, *++curr_ptr))
6386 {}
6387 /* If there was nothing past the variable, skip condition part */
6388 if (curr_ptr == expr_end)
6389 goto NO_COMPARE;
6390
6391 enum block_op operand= find_operand(curr_ptr);
6392 if (operand == ILLEG_OP)
6393 die("Found junk '%.*s' after $variable in condition",
6394 (int)(expr_end - curr_ptr), curr_ptr);
6395
6396 /* We could silently allow this, but may be confusing */
6397 if (not_expr)
6398 die("Negation and comparison should not be combined, please rewrite");
6399
6400 /* Skip the 1 or 2 chars of the operand, then white space */
6401 if (operand == LT_OP || operand == GT_OP)
6402 {
6403 curr_ptr++;
6404 }
6405 else
6406 {
6407 curr_ptr+= 2;
6408 }
6409 while (my_isspace(charset_info, *curr_ptr))
6410 curr_ptr++;
6411 if (curr_ptr == expr_end)
6412 die("Missing right operand in comparison");
6413
6414 /* Strip off trailing white space */
6415 while (my_isspace(charset_info, expr_end[-1]))
6416 expr_end--;
6417 /* strip off ' or " around the string */
6418 if (*curr_ptr == '\'' || *curr_ptr == '"')
6419 {
6420 if (expr_end[-1] != *curr_ptr)
6421 die("Unterminated string value");
6422 curr_ptr++;
6423 expr_end--;
6424 }
6425 VAR v2;
6426 var_init(&v2,0,0,0,0);
6427 eval_expr(&v2, curr_ptr, &expr_end);
6428
6429 if ((operand!=EQ_OP && operand!=NE_OP) && ! (v.is_int && v2.is_int))
6430 die("Only == and != are supported for string values");
6431
6432 /* Now we overwrite the first variable with 0 or 1 (for false or true) */
6433
6434 switch (operand)
6435 {
6436 case EQ_OP:
6437 if (v.is_int)
6438 v.int_val= (v2.is_int && v2.int_val == v.int_val);
6439 else
6440 v.int_val= !strcmp (v.str_val, v2.str_val);
6441 break;
6442
6443 case NE_OP:
6444 if (v.is_int)
6445 v.int_val= ! (v2.is_int && v2.int_val == v.int_val);
6446 else
6447 v.int_val= (strcmp (v.str_val, v2.str_val) != 0);
6448 break;
6449
6450 case LT_OP:
6451 v.int_val= (v.int_val < v2.int_val);
6452 break;
6453 case LE_OP:
6454 v.int_val= (v.int_val <= v2.int_val);
6455 break;
6456 case GT_OP:
6457 v.int_val= (v.int_val > v2.int_val);
6458 break;
6459 case GE_OP:
6460 v.int_val= (v.int_val >= v2.int_val);
6461 break;
6462 case ILLEG_OP:
6463 die("Impossible operator, this cannot happen");
6464 }
6465
6466 v.is_int= TRUE;
6467 var_free(&v2);
6468 } else
6469 {
6470 if (*expr_start != '`' && ! my_isdigit(charset_info, *expr_start))
6471 die("Expression in if/while must beging with $, ` or a number");
6472 eval_expr(&v, expr_start, &expr_end);
6473 }
6474
6475 NO_COMPARE:
6476 /* Define inner block */
6477 cur_block++;
6478 cur_block->cmd= cmd;
6479 if (v.is_int)
6480 {
6481 cur_block->ok= (v.int_val != 0);
6482 } else
6483 /* Any non-empty string which does not begin with 0 is also TRUE */
6484 {
6485 p= v.str_val;
6486 /* First skip any leading white space or unary -+ */
6487 while (*p && ((my_isspace(charset_info, *p) || *p == '-' || *p == '+')))
6488 p++;
6489
6490 cur_block->ok= (*p && *p != '0') ? TRUE : FALSE;
6491 }
6492
6493 if (not_expr)
6494 cur_block->ok = !cur_block->ok;
6495
6496 if (cur_block->ok)
6497 {
6498 cur_block->delim[0]= '\0';
6499 } else
6500 {
6501 /* Remember "old" delimiter if entering a false if block */
6502 strcpy (cur_block->delim, delimiter);
6503 }
6504
6505 DBUG_PRINT("info", ("OK: %d", cur_block->ok));
6506
6507 var_free(&v);
6508 DBUG_VOID_RETURN;
6509}
6510
6511
6512void do_delimiter(struct st_command* command)
6513{
6514 char* p= command->first_argument;
6515 DBUG_ENTER("do_delimiter");
6516 DBUG_PRINT("enter", ("first_argument: %s", command->first_argument));
6517
6518 while (*p && my_isspace(charset_info, *p))
6519 p++;
6520
6521 if (!(*p))
6522 die("Can't set empty delimiter");
6523
6524 delimiter_length= (uint)(strmake_buf(delimiter, p) - delimiter);
6525
6526 DBUG_PRINT("exit", ("delimiter: %s", delimiter));
6527 command->last_argument= p + delimiter_length;
6528 DBUG_VOID_RETURN;
6529}
6530
6531
6532my_bool match_delimiter(int c, const char *delim, size_t length)
6533{
6534 uint i;
6535 char tmp[MAX_DELIMITER_LENGTH];
6536
6537 if (c != *delim)
6538 return 0;
6539
6540 for (i= 1; i < length &&
6541 (c= my_getc(cur_file->file)) == *(delim + i);
6542 i++)
6543 tmp[i]= c;
6544
6545 if (i == length)
6546 return 1; /* Found delimiter */
6547
6548 /* didn't find delimiter, push back things that we read */
6549 my_ungetc(c);
6550 while (i > 1)
6551 my_ungetc(tmp[--i]);
6552 return 0;
6553}
6554
6555
6556my_bool end_of_query(int c)
6557{
6558 return match_delimiter(c, delimiter, delimiter_length);
6559}
6560
6561
6562static inline bool is_escape_char(char c, char in_string)
6563{
6564 if (c != '\\' || in_string == '`') return false;
6565 if (!cur_con) return true;
6566 uint server_status= cur_con->mysql->server_status;
6567 if (server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) return false;
6568 return !(server_status & SERVER_STATUS_ANSI_QUOTES && in_string == '"');
6569}
6570
6571
6572/*
6573 Read one "line" from the file
6574
6575 SYNOPSIS
6576 read_line
6577 buf buffer for the read line
6578 size size of the buffer i.e max size to read
6579
6580 DESCRIPTION
6581 This function actually reads several lines and adds them to the
6582 buffer buf. It continues to read until it finds what it believes
6583 is a complete query.
6584
6585 Normally that means it will read lines until it reaches the
6586 "delimiter" that marks end of query. Default delimiter is ';'
6587 The function should be smart enough not to detect delimiter's
6588 found inside strings surrounded with '"' and '\'' escaped strings.
6589
6590 If the first line in a query starts with '#' or '-' this line is treated
6591 as a comment. A comment is always terminated when end of line '\n' is
6592 reached.
6593
6594*/
6595
6596int read_line(char *buf, int size)
6597{
6598 char c, last_quote=0, last_char= 0;
6599 char *p= buf, *buf_end= buf + size - 1;
6600 int skip_char= 0;
6601 my_bool have_slash= FALSE;
6602
6603 enum {R_NORMAL, R_Q, R_SLASH_IN_Q,
6604 R_COMMENT, R_LINE_START} state= R_LINE_START;
6605 DBUG_ENTER("read_line");
6606
6607 start_lineno= cur_file->lineno;
6608 DBUG_PRINT("info", ("Starting to read at lineno: %d", start_lineno));
6609 for (; p < buf_end ;)
6610 {
6611 skip_char= 0;
6612 c= my_getc(cur_file->file);
6613 if (feof(cur_file->file))
6614 {
6615 found_eof:
6616 if (cur_file->file != stdin)
6617 {
6618 fclose(cur_file->file);
6619 cur_file->file= 0;
6620 }
6621 my_free(cur_file->file_name);
6622 cur_file->file_name= 0;
6623 if (cur_file == file_stack)
6624 {
6625 /* We're back at the first file, check if
6626 all { have matching }
6627 */
6628 if (cur_block != block_stack)
6629 die("Missing end of block");
6630
6631 *p= 0;
6632 DBUG_PRINT("info", ("end of file at line %d", cur_file->lineno));
6633 DBUG_RETURN(1);
6634 }
6635 cur_file--;
6636 start_lineno= cur_file->lineno;
6637 continue;
6638 }
6639
6640 if (c == '\n')
6641 {
6642 /* Line counting is independent of state */
6643 cur_file->lineno++;
6644
6645 /* Convert cr/lf to lf */
6646 if (p != buf && *(p-1) == '\r')
6647 p--;
6648 }
6649
6650 switch(state) {
6651 case R_NORMAL:
6652 if (end_of_query(c))
6653 {
6654 *p= 0;
6655 DBUG_PRINT("exit", ("Found delimiter '%s' at line %d",
6656 delimiter, cur_file->lineno));
6657 DBUG_RETURN(0);
6658 }
6659 else if ((c == '{' &&
6660 (!my_strnncoll_simple(charset_info, (const uchar*) "while", 5,
6661 (uchar*) buf, MY_MIN(5, p - buf), 0) ||
6662 !my_strnncoll_simple(charset_info, (const uchar*) "if", 2,
6663 (uchar*) buf, MY_MIN(2, p - buf), 0))))
6664 {
6665 /* Only if and while commands can be terminated by { */
6666 *p++= c;
6667 *p= 0;
6668 DBUG_PRINT("exit", ("Found '{' indicating start of block at line %d",
6669 cur_file->lineno));
6670 DBUG_RETURN(0);
6671 }
6672 else if (c == '\'' || c == '"' || c == '`')
6673 {
6674 if (! have_slash)
6675 {
6676 last_quote= c;
6677 state= R_Q;
6678 }
6679 }
6680 have_slash= is_escape_char(c, last_quote);
6681 break;
6682
6683 case R_COMMENT:
6684 if (c == '\n')
6685 {
6686 /* Comments are terminated by newline */
6687 *p= 0;
6688 DBUG_PRINT("exit", ("Found newline in comment at line: %d",
6689 cur_file->lineno));
6690 DBUG_RETURN(0);
6691 }
6692 break;
6693
6694 case R_LINE_START:
6695 if (c == '#' || c == '-')
6696 {
6697 /* A # or - in the first position of the line - this is a comment */
6698 state = R_COMMENT;
6699 }
6700 else if (my_isspace(charset_info, c))
6701 {
6702 if (c == '\n')
6703 {
6704 if (last_char == '\n')
6705 {
6706 /* Two new lines in a row, return empty line */
6707 DBUG_PRINT("info", ("Found two new lines in a row"));
6708 *p++= c;
6709 *p= 0;
6710 DBUG_RETURN(0);
6711 }
6712
6713 /* Query hasn't started yet */
6714 start_lineno= cur_file->lineno;
6715 DBUG_PRINT("info", ("Query hasn't started yet, start_lineno: %d",
6716 start_lineno));
6717 }
6718
6719 /* Skip all space at beginning of line */
6720 skip_char= 1;
6721 }
6722 else if (end_of_query(c))
6723 {
6724 *p= 0;
6725 DBUG_PRINT("exit", ("Found delimiter '%s' at line: %d",
6726 delimiter, cur_file->lineno));
6727 DBUG_RETURN(0);
6728 }
6729 else if (c == '}')
6730 {
6731 /* A "}" need to be by itself in the beginning of a line to terminate */
6732 *p++= c;
6733 *p= 0;
6734 DBUG_PRINT("exit", ("Found '}' in beginning of a line at line: %d",
6735 cur_file->lineno));
6736 DBUG_RETURN(0);
6737 }
6738 else if (c == '\'' || c == '"' || c == '`')
6739 {
6740 last_quote= c;
6741 state= R_Q;
6742 }
6743 else
6744 state= R_NORMAL;
6745 break;
6746
6747 case R_Q:
6748 if (c == last_quote)
6749 state= R_NORMAL;
6750 else if (is_escape_char(c, last_quote))
6751 state= R_SLASH_IN_Q;
6752 break;
6753
6754 case R_SLASH_IN_Q:
6755 state= R_Q;
6756 break;
6757
6758 }
6759
6760 last_char= c;
6761
6762 if (!skip_char)
6763 {
6764 *p++= c;
6765 if (use_mb(charset_info))
6766 {
6767 const char *mb_start= p - 1;
6768 /* Could be a multibyte character */
6769 /* See a similar code in "sql_load.cc" */
6770 for ( ; p < buf_end; )
6771 {
6772 int charlen= my_charlen(charset_info, mb_start, p);
6773 if (charlen > 0)
6774 break; /* Full character */
6775 if (MY_CS_IS_TOOSMALL(charlen))
6776 {
6777 /* We give up if multibyte character is started but not */
6778 /* completed before we pass buf_end */
6779 c= my_getc(cur_file->file);
6780 if (feof(cur_file->file))
6781 goto found_eof;
6782 *p++ = c;
6783 continue;
6784 }
6785 DBUG_ASSERT(charlen == MY_CS_ILSEQ);
6786 /* It was not a multiline char, push back the characters */
6787 /* We leave first 'c', i.e. pretend it was a normal char */
6788 while (p - 1 > mb_start)
6789 my_ungetc(*--p);
6790 break;
6791 }
6792 }
6793 }
6794 }
6795 die("The input buffer is too small for this query.\n"
6796 "check your query or increase MAX_QUERY and recompile");
6797 DBUG_RETURN(0);
6798}
6799
6800
6801/*
6802 Convert the read query to result format version 1
6803
6804 That is: After newline, all spaces need to be skipped
6805 unless the previous char was a quote
6806
6807 This is due to an old bug that has now been fixed, but the
6808 version 1 output format is preserved by using this function
6809
6810*/
6811
6812void convert_to_format_v1(char* query)
6813{
6814 int last_c_was_quote= 0;
6815 char *p= query, *to= query;
6816 char *end= strend(query);
6817 char last_c;
6818
6819 while (p <= end)
6820 {
6821 if (*p == '\n' && !last_c_was_quote)
6822 {
6823 *to++ = *p++; /* Save the newline */
6824
6825 /* Skip any spaces on next line */
6826 while (*p && my_isspace(charset_info, *p))
6827 p++;
6828
6829 last_c_was_quote= 0;
6830 }
6831 else if (*p == '\'' || *p == '"' || *p == '`')
6832 {
6833 last_c= *p;
6834 *to++ = *p++;
6835
6836 /* Copy anything until the next quote of same type */
6837 while (*p && *p != last_c)
6838 *to++ = *p++;
6839
6840 *to++ = *p++;
6841
6842 last_c_was_quote= 1;
6843 }
6844 else
6845 {
6846 *to++ = *p++;
6847 last_c_was_quote= 0;
6848 }
6849 }
6850}
6851
6852
6853/*
6854 Check for unexpected "junk" after the end of query
6855 This is normally caused by missing delimiters or when
6856 switching between different delimiters
6857*/
6858
6859void check_eol_junk_line(const char *line)
6860{
6861 const char *p= line;
6862 DBUG_ENTER("check_eol_junk_line");
6863 DBUG_PRINT("enter", ("line: %s", line));
6864
6865 /* Check for extra delimiter */
6866 if (*p && !strncmp(p, delimiter, delimiter_length))
6867 die("Extra delimiter \"%s\" found", delimiter);
6868
6869 /* Allow trailing # comment */
6870 if (*p && *p != '#')
6871 {
6872 if (*p == '\n')
6873 die("Missing delimiter");
6874 die("End of line junk detected: \"%s\"", p);
6875 }
6876 DBUG_VOID_RETURN;
6877}
6878
6879void check_eol_junk(const char *eol)
6880{
6881 const char *p= eol;
6882 DBUG_ENTER("check_eol_junk");
6883 DBUG_PRINT("enter", ("eol: %s", eol));
6884
6885 /* Skip past all spacing chars and comments */
6886 while (*p && (my_isspace(charset_info, *p) || *p == '#' || *p == '\n'))
6887 {
6888 /* Skip past comments started with # and ended with newline */
6889 if (*p && *p == '#')
6890 {
6891 p++;
6892 while (*p && *p != '\n')
6893 p++;
6894 }
6895
6896 /* Check this line */
6897 if (*p && *p == '\n')
6898 check_eol_junk_line(p);
6899
6900 if (*p)
6901 p++;
6902 }
6903
6904 check_eol_junk_line(p);
6905
6906 DBUG_VOID_RETURN;
6907}
6908
6909
6910bool is_delimiter(const char* p)
6911{
6912 uint match= 0;
6913 char* delim= delimiter;
6914 while (*p && *p == *delim++)
6915 {
6916 match++;
6917 p++;
6918 }
6919
6920 return (match == delimiter_length);
6921}
6922
6923
6924/*
6925 Create a command from a set of lines
6926
6927 SYNOPSIS
6928 read_command()
6929 command_ptr pointer where to return the new query
6930
6931 DESCRIPTION
6932 Converts lines returned by read_line into a command, this involves
6933 parsing the first word in the read line to find the command type.
6934
6935 A -- comment may contain a valid query as the first word after the
6936 comment start. Thus it's always checked to see if that is the case.
6937 The advantage with this approach is to be able to execute commands
6938 terminated by new line '\n' regardless how many "delimiter" it contain.
6939*/
6940
6941#define MAX_QUERY (256*1024*2) /* 256K -- a test in sp-big is >128K */
6942static char read_command_buf[MAX_QUERY];
6943
6944int read_command(struct st_command** command_ptr)
6945{
6946 char *p= read_command_buf;
6947 struct st_command* command;
6948 DBUG_ENTER("read_command");
6949
6950 if (parser.current_line < parser.read_lines)
6951 {
6952 get_dynamic(&q_lines, command_ptr, parser.current_line) ;
6953 DBUG_PRINT("info", ("query: %s", (*command_ptr)->query));
6954 DBUG_RETURN(0);
6955 }
6956 if (!(*command_ptr= command=
6957 (struct st_command*) my_malloc(sizeof(*command),
6958 MYF(MY_WME|MY_ZEROFILL))) ||
6959 insert_dynamic(&q_lines, &command))
6960 die("Out of memory");
6961 command->type= Q_UNKNOWN;
6962
6963 read_command_buf[0]= 0;
6964 if (read_line(read_command_buf, sizeof(read_command_buf)))
6965 {
6966 check_eol_junk(read_command_buf);
6967 DBUG_RETURN(1);
6968 }
6969
6970 if (opt_result_format_version == 1)
6971 convert_to_format_v1(read_command_buf);
6972
6973 DBUG_PRINT("info", ("query: '%s'", read_command_buf));
6974 if (*p == '#')
6975 {
6976 command->type= Q_COMMENT;
6977 }
6978 else if (p[0] == '-' && p[1] == '-')
6979 {
6980 command->type= Q_COMMENT_WITH_COMMAND;
6981 p+= 2; /* Skip past -- */
6982 }
6983 else if (*p == '\n')
6984 {
6985 command->type= Q_EMPTY_LINE;
6986 }
6987
6988 /* Skip leading spaces */
6989 while (*p && my_isspace(charset_info, *p))
6990 p++;
6991
6992 if (!(command->query_buf= command->query= my_strdup(p, MYF(MY_WME))))
6993 die("Out of memory");
6994
6995 /*
6996 Calculate first word length(the command), terminated
6997 by 'space' , '(' or 'delimiter' */
6998 p= command->query;
6999 while (*p && !my_isspace(charset_info, *p) && *p != '(' && !is_delimiter(p))
7000 p++;
7001 command->first_word_len= (uint) (p - command->query);
7002 DBUG_PRINT("info", ("first_word: %.*s",
7003 command->first_word_len, command->query));
7004
7005 /* Skip spaces between command and first argument */
7006 while (*p && my_isspace(charset_info, *p))
7007 p++;
7008 command->first_argument= p;
7009
7010 command->end= strend(command->query);
7011 command->query_len= (int)(command->end - command->query);
7012 parser.read_lines++;
7013 DBUG_RETURN(0);
7014}
7015
7016
7017static struct my_option my_long_options[] =
7018{
7019 {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
7020 0, 0, 0, 0, 0, 0},
7021 {"basedir", 'b', "Basedir for tests.", &opt_basedir,
7022 &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7023 {"character-sets-dir", 0,
7024 "Directory for character set files.", &opt_charsets_dir,
7025 &opt_charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7026 {"compress", 'C', "Use the compressed server/client protocol.",
7027 &opt_compress, &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
7028 0, 0, 0},
7029 {"continue-on-error", 0,
7030 "Continue test even if we got an error. "
7031 "This is mostly useful when testing a storage engine to see what from a test file it can execute, "
7032 "or to find all syntax errors in a newly created big test file",
7033 &opt_continue_on_error, &opt_continue_on_error, 0,
7034 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
7035 {"cursor-protocol", 0, "Use cursors for prepared statements.",
7036 &cursor_protocol, &cursor_protocol, 0,
7037 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
7038 {"database", 'D', "Database to use.", &opt_db, &opt_db, 0,
7039 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7040#ifdef DBUG_OFF
7041 {"debug", '#', "This is a non-debug version. Catch this and exit",
7042 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
7043#else
7044 {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
7045 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
7046#endif
7047 {"debug-check", 0, "Check memory and open file usage at exit.",
7048 &debug_check_flag, &debug_check_flag, 0,
7049 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
7050 {"debug-info", 0, "Print some debug info at exit.",
7051 &debug_info_flag, &debug_info_flag,
7052 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
7053 {"host", 'h', "Connect to host.", &opt_host, &opt_host, 0,
7054 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7055 {"prologue", 0, "Include SQL before each test case.", &opt_prologue,
7056 &opt_prologue, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7057 {"logdir", OPT_LOG_DIR, "Directory for log files", &opt_logdir,
7058 &opt_logdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7059 {"mark-progress", 0,
7060 "Write line number and elapsed time to <testname>.progress.",
7061 &opt_mark_progress, &opt_mark_progress, 0,
7062 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
7063 {"max-connect-retries", 0,
7064 "Maximum number of attempts to connect to server.",
7065 &opt_max_connect_retries, &opt_max_connect_retries, 0,
7066 GET_INT, REQUIRED_ARG, 500, 1, 10000, 0, 0, 0},
7067 {"max-connections", 0,
7068 "Max number of open connections to server",
7069 &opt_max_connections, &opt_max_connections, 0,
7070 GET_INT, REQUIRED_ARG, DEFAULT_MAX_CONN, 8, 5120, 0, 0, 0},
7071 {"password", 'p', "Password to use when connecting to server.",
7072 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
7073 {"protocol", OPT_MYSQL_PROTOCOL, "The protocol of connection (tcp,socket,pipe,memory).",
7074 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7075 {"port", 'P', "Port number to use for connection or 0 for default to, in "
7076 "order of preference, my.cnf, $MYSQL_TCP_PORT, "
7077#if MYSQL_PORT_DEFAULT == 0
7078 "/etc/services, "
7079#endif
7080 "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
7081 &opt_port, &opt_port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7082 {"ps-protocol", 0,
7083 "Use prepared-statement protocol for communication.",
7084 &ps_protocol, &ps_protocol, 0,
7085 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
7086 {"non-blocking-api", 0,
7087 "Use the non-blocking client API for communication.",
7088 &non_blocking_api_enabled, &non_blocking_api_enabled, 0,
7089 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
7090 {"quiet", 's', "Suppress all normal output.", &silent,
7091 &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
7092 {"record", 'r', "Record output of test_file into result file.",
7093 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
7094 {"result-file", 'R', "Read/store result from/in this file.",
7095 &result_file_name, &result_file_name, 0,
7096 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7097 {"result-format-version", OPT_RESULT_FORMAT_VERSION,
7098 "Version of the result file format to use",
7099 &opt_result_format_version,
7100 &opt_result_format_version, 0,
7101 GET_INT, REQUIRED_ARG, 1, 1, 2, 0, 0, 0},
7102 {"server-arg", 'A', "Send option value to embedded server as a parameter.",
7103 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7104 {"server-file", 'F', "Read embedded server arguments from file.",
7105 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7106 {"shared-memory-base-name", 0,
7107 "Base name of shared memory.", &shared_memory_base_name,
7108 &shared_memory_base_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0,
7109 0, 0, 0},
7110 {"silent", 's', "Suppress all normal output. Synonym for --quiet.",
7111 &silent, &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
7112 {"sleep", 'T', "Always sleep this many seconds on sleep commands.",
7113 &opt_sleep, &opt_sleep, 0, GET_INT, REQUIRED_ARG, -1, -1, 0,
7114 0, 0, 0},
7115 {"socket", 'S', "The socket file to use for connection.",
7116 &unix_sock, &unix_sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0,
7117 0, 0, 0},
7118 {"sp-protocol", 0, "Use stored procedures for select.",
7119 &sp_protocol, &sp_protocol, 0,
7120 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
7121#include "sslopt-longopts.h"
7122 {"tail-lines", 0,
7123 "Number of lines of the result to include in a failure report.",
7124 &opt_tail_lines, &opt_tail_lines, 0,
7125 GET_INT, REQUIRED_ARG, 0, 0, 10000, 0, 0, 0},
7126 {"test-file", 'x', "Read test from/in this file (default stdin).",
7127 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7128 {"timer-file", 'm', "File where the timing in microseconds is stored.",
7129 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7130 {"tmpdir", 't', "Temporary directory where sockets are put.",
7131 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7132 {"user", 'u', "User for login.", &opt_user, &opt_user, 0,
7133 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7134 {"verbose", 'v', "Write more.", &verbose, &verbose, 0,
7135 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
7136 {"version", 'V', "Output version information and exit.",
7137 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
7138 {"view-protocol", 0, "Use views for select.",
7139 &view_protocol, &view_protocol, 0,
7140 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
7141 {"connect_timeout", 0,
7142 "Number of seconds before connection timeout.",
7143 &opt_connect_timeout, &opt_connect_timeout, 0, GET_UINT, REQUIRED_ARG,
7144 120, 0, 3600 * 12, 0, 0, 0},
7145 {"wait_for_pos_timeout", 0,
7146 "Number of seconds to wait for master_pos_wait",
7147 &opt_wait_for_pos_timeout, &opt_wait_for_pos_timeout, 0, GET_UINT,
7148 REQUIRED_ARG, 300, 0, 3600 * 12, 0, 0, 0},
7149 {"plugin_dir", 0, "Directory for client-side plugins.",
7150 &opt_plugin_dir, &opt_plugin_dir, 0,
7151 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7152 {"overlay-dir", 0, "Overlay directory.", &opt_overlay_dir,
7153 &opt_overlay_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7154 {"suite-dir", 0, "Suite directory.", &opt_suite_dir,
7155 &opt_suite_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
7156 { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
7157};
7158
7159
7160void print_version(void)
7161{
7162 printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname,MTEST_VERSION,
7163 MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE);
7164}
7165
7166void usage()
7167{
7168 print_version();
7169 puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
7170 printf("Runs a test against the mysql server and compares output with a results file.\n\n");
7171 printf("Usage: %s [OPTIONS] [database] < test_file\n", my_progname);
7172 print_defaults("my",load_default_groups);
7173 puts("");
7174 my_print_help(my_long_options);
7175 my_print_variables(my_long_options);
7176}
7177
7178
7179/*
7180 Read arguments for embedded server and put them into
7181 embedded_server_args[]
7182*/
7183
7184void read_embedded_server_arguments(const char *name)
7185{
7186 char argument[1024],buff[FN_REFLEN], *str=0;
7187 FILE *file;
7188
7189 if (!test_if_hard_path(name))
7190 {
7191 strxmov(buff, opt_basedir, name, NullS);
7192 name=buff;
7193 }
7194 fn_format(buff, name, "", "", MY_UNPACK_FILENAME);
7195
7196 if (!embedded_server_arg_count)
7197 {
7198 embedded_server_arg_count=1;
7199 embedded_server_args[0]= const_cast<char*>(""); /* Progname */
7200 }
7201 if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
7202 die("Failed to open file '%s'", buff);
7203
7204 while (embedded_server_arg_count < MAX_EMBEDDED_SERVER_ARGS &&
7205 (str=fgets(argument,sizeof(argument), file)))
7206 {
7207 *(strend(str)-1)=0; /* Remove end newline */
7208 if (!(embedded_server_args[embedded_server_arg_count]=
7209 my_strdup(str, MYF(MY_WME))))
7210 {
7211 my_fclose(file,MYF(0));
7212 die("Out of memory");
7213
7214 }
7215 embedded_server_arg_count++;
7216 }
7217 my_fclose(file,MYF(0));
7218 if (str)
7219 die("Too many arguments in option file: %s",name);
7220
7221 return;
7222}
7223
7224
7225static my_bool
7226get_one_option(int optid, const struct my_option *opt, char *argument)
7227{
7228 switch(optid) {
7229 case '#':
7230#ifndef DBUG_OFF
7231 DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysqltest.trace");
7232 debug_check_flag= 1;
7233 debug_info_flag= 1;
7234#endif
7235 break;
7236 case 'r':
7237 record = 1;
7238 break;
7239 case 'x':
7240 {
7241 char buff[FN_REFLEN];
7242 if (!test_if_hard_path(argument))
7243 {
7244 strxmov(buff, opt_basedir, argument, NullS);
7245 argument= buff;
7246 }
7247 fn_format(buff, argument, "", "", MY_UNPACK_FILENAME);
7248 DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0);
7249 if (!(cur_file->file=
7250 fopen(buff, "rb")))
7251 die("Could not open '%s' for reading, errno: %d", buff, errno);
7252 cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
7253 cur_file->lineno= 1;
7254 break;
7255 }
7256 case 'm':
7257 {
7258 static char buff[FN_REFLEN];
7259 if (!test_if_hard_path(argument))
7260 {
7261 strxmov(buff, opt_basedir, argument, NullS);
7262 argument= buff;
7263 }
7264 fn_format(buff, argument, "", "", MY_UNPACK_FILENAME);
7265 timer_file= buff;
7266 unlink(timer_file); /* Ignore error, may not exist */
7267 break;
7268 }
7269 case 'p':
7270 if (argument == disabled_my_option)
7271 argument= const_cast<char*>(""); // Don't require password
7272 if (argument)
7273 {
7274 my_free(opt_pass);
7275 opt_pass= my_strdup(argument, MYF(MY_FAE));
7276 while (*argument) *argument++= 'x'; /* Destroy argument */
7277 tty_password= 0;
7278 }
7279 else
7280 tty_password= 1;
7281 break;
7282#include <sslopt-case.h>
7283 case 't':
7284 strnmov(TMPDIR, argument, sizeof(TMPDIR));
7285 break;
7286 case 'A':
7287 if (!embedded_server_arg_count)
7288 {
7289 embedded_server_arg_count=1;
7290 embedded_server_args[0]= const_cast<char*>("");
7291 }
7292 if (embedded_server_arg_count == MAX_EMBEDDED_SERVER_ARGS-1 ||
7293 !(embedded_server_args[embedded_server_arg_count++]=
7294 my_strdup(argument, MYF(MY_FAE))))
7295 {
7296 die("Can't use server argument");
7297 }
7298 break;
7299 case OPT_LOG_DIR:
7300 /* Check that the file exists */
7301 if (access(opt_logdir, F_OK) != 0)
7302 die("The specified log directory does not exist: '%s'", opt_logdir);
7303 break;
7304 case 'F':
7305 read_embedded_server_arguments(argument);
7306 break;
7307 case OPT_RESULT_FORMAT_VERSION:
7308 set_result_format_version(opt_result_format_version);
7309 break;
7310 case 'V':
7311 print_version();
7312 exit(0);
7313 case OPT_MYSQL_PROTOCOL:
7314#ifndef EMBEDDED_LIBRARY
7315 if ((opt_protocol= find_type_with_warning(argument, &sql_protocol_typelib,
7316 opt->name)) <= 0)
7317 exit(1);
7318#endif
7319 break;
7320 case '?':
7321 usage();
7322 exit(0);
7323 }
7324 return 0;
7325}
7326
7327
7328int parse_args(int argc, char **argv)
7329{
7330 load_defaults_or_exit("my", load_default_groups, &argc, &argv);
7331 default_argv= argv;
7332
7333 if ((handle_options(&argc, &argv, my_long_options, get_one_option)))
7334 exit(1);
7335
7336 if (argc > 1)
7337 {
7338 usage();
7339 exit(1);
7340 }
7341 if (argc == 1)
7342 opt_db= *argv;
7343 if (tty_password)
7344 opt_pass= get_tty_password(NullS); /* purify tested */
7345 if (debug_info_flag)
7346 my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
7347 if (debug_check_flag)
7348 my_end_arg|= MY_CHECK_ERROR;
7349
7350 if (global_subst != NULL)
7351 {
7352 char *comma= strstr(global_subst, ",");
7353 if (comma == NULL)
7354 die("wrong --global-subst, must be X,Y");
7355 memcpy(global_subst_from, global_subst, (comma-global_subst));
7356 global_subst_from[comma-global_subst]= 0;
7357 memcpy(global_subst_to, comma+1, strlen(comma));
7358 }
7359
7360 if (!opt_suite_dir)
7361 opt_suite_dir= "./";
7362 suite_dir_len= strlen(opt_suite_dir);
7363 overlay_dir_len= opt_overlay_dir ? strlen(opt_overlay_dir) : 0;
7364
7365 if (!record)
7366 {
7367 /* Check that the result file exists */
7368 if (result_file_name && access(result_file_name, F_OK) != 0)
7369 die("The specified result file '%s' does not exist",
7370 result_file_name);
7371 }
7372
7373 return 0;
7374}
7375
7376/*
7377 Write the content of str into file
7378
7379 SYNOPSIS
7380 str_to_file2
7381 fname - name of file to truncate/create and write to
7382 str - content to write to file
7383 size - size of content witten to file
7384 append - append to file instead of overwriting old file
7385*/
7386
7387void str_to_file2(const char *fname, char *str, size_t size, my_bool append)
7388{
7389 int fd;
7390 char buff[FN_REFLEN];
7391 int flags= O_WRONLY | O_CREAT;
7392 if (!test_if_hard_path(fname))
7393 {
7394 strxmov(buff, opt_basedir, fname, NullS);
7395 fname= buff;
7396 }
7397 fn_format(buff, fname, "", "", MY_UNPACK_FILENAME);
7398
7399 if (!append)
7400 flags|= O_TRUNC;
7401 if ((fd= my_open(buff, flags,
7402 MYF(MY_WME | MY_FFNF))) < 0)
7403 die("Could not open '%s' for writing, errno: %d", buff, errno);
7404 if (append && my_seek(fd, 0, SEEK_END, MYF(0)) == MY_FILEPOS_ERROR)
7405 die("Could not find end of file '%s', errno: %d", buff, errno);
7406 if (my_write(fd, (uchar*)str, size, MYF(MY_WME|MY_FNABP)))
7407 die("write failed, errno: %d", errno);
7408 my_close(fd, MYF(0));
7409}
7410
7411/*
7412 Write the content of str into file
7413
7414 SYNOPSIS
7415 str_to_file
7416 fname - name of file to truncate/create and write to
7417 str - content to write to file
7418 size - size of content witten to file
7419*/
7420
7421void str_to_file(const char *fname, char *str, size_t size)
7422{
7423 str_to_file2(fname, str, size, FALSE);
7424}
7425
7426
7427void check_regerr(regex_t* r, int err)
7428{
7429 char err_buf[1024];
7430
7431 if (err)
7432 {
7433 regerror(err,r,err_buf,sizeof(err_buf));
7434 die("Regex error: %s\n", err_buf);
7435 }
7436}
7437
7438
7439#ifdef _WIN32
7440
7441DYNAMIC_ARRAY patterns;
7442
7443/*
7444 init_win_path_patterns
7445
7446 DESCRIPTION
7447 Setup string patterns that will be used to detect filenames that
7448 needs to be converted from Win to Unix format
7449
7450*/
7451
7452void init_win_path_patterns()
7453{
7454 /* List of string patterns to match in order to find paths */
7455 const char* paths[] = { "$MYSQL_TEST_DIR",
7456 "$MYSQL_TMP_DIR",
7457 "$MYSQLTEST_VARDIR",
7458 "$MASTER_MYSOCK",
7459 "$MYSQL_SHAREDIR",
7460 "$MYSQL_LIBDIR",
7461 "./test/" };
7462 int num_paths= sizeof(paths)/sizeof(char*);
7463 int i;
7464 char* p;
7465
7466 DBUG_ENTER("init_win_path_patterns");
7467
7468 my_init_dynamic_array(&patterns, sizeof(const char*), 16, 16, MYF(0));
7469
7470 /* Loop through all paths in the array */
7471 for (i= 0; i < num_paths; i++)
7472 {
7473 VAR* v;
7474 if (*(paths[i]) == '$')
7475 {
7476 v= var_get(paths[i], 0, 0, 0);
7477 p= my_strdup(v->str_val, MYF(MY_FAE));
7478 }
7479 else
7480 p= my_strdup(paths[i], MYF(MY_FAE));
7481
7482 /* Don't insert zero length strings in patterns array */
7483 if (strlen(p) == 0)
7484 {
7485 my_free(p);
7486 continue;
7487 }
7488
7489 if (insert_dynamic(&patterns, &p))
7490 die("Out of memory");
7491
7492 DBUG_PRINT("info", ("p: %s", p));
7493 while (*p)
7494 {
7495 if (*p == '/')
7496 *p='\\';
7497 p++;
7498 }
7499 }
7500 DBUG_VOID_RETURN;
7501}
7502
7503void free_win_path_patterns()
7504{
7505 uint i= 0;
7506 for (i=0 ; i < patterns.elements ; i++)
7507 {
7508 const char** pattern= dynamic_element(&patterns, i, const char**);
7509 my_free((void *) *pattern);
7510 }
7511 delete_dynamic(&patterns);
7512}
7513#endif
7514
7515/*
7516 fix_win_paths
7517
7518 DESCRIPTION
7519 Search the string 'val' for the patterns that are known to be
7520 strings that contain filenames. Convert all \ to / in the
7521 filenames that are found.
7522
7523 Ex:
7524 val = 'Error "c:\mysql\mysql-test\var\test\t1.frm" didn't exist'
7525 => $MYSQL_TEST_DIR is found by strstr
7526 => all \ from c:\mysql\m... until next space is converted into /
7527*/
7528
7529void fix_win_paths(char *val, size_t len)
7530{
7531#ifdef _WIN32
7532 uint i;
7533 char *p;
7534
7535 DBUG_ENTER("fix_win_paths");
7536 for (i= 0; i < patterns.elements; i++)
7537 {
7538 const char** pattern= dynamic_element(&patterns, i, const char**);
7539 DBUG_PRINT("info", ("pattern: %s", *pattern));
7540
7541 /* Search for the path in string */
7542 while ((p= strstr(val, *pattern)))
7543 {
7544 DBUG_PRINT("info", ("Found %s in val p: %s", *pattern, p));
7545
7546 while (*p && !my_isspace(charset_info, *p))
7547 {
7548 if (*p == '\\')
7549 *p= '/';
7550 p++;
7551 }
7552 DBUG_PRINT("info", ("Converted \\ to /, p: %s", p));
7553 }
7554 }
7555 DBUG_PRINT("exit", (" val: %s, len: %zu", val, len));
7556 DBUG_VOID_RETURN;
7557#endif
7558}
7559
7560
7561
7562/*
7563 Append the result for one field to the dynamic string ds
7564*/
7565
7566void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD* field,
7567 char* val, size_t len, my_bool is_null)
7568{
7569 char null[]= "NULL";
7570
7571 if (col_idx < max_replace_column && replace_column[col_idx])
7572 {
7573 val= replace_column[col_idx];
7574 len= strlen(val);
7575 }
7576 else if (is_null)
7577 {
7578 val= null;
7579 len= 4;
7580 }
7581#ifdef _WIN32
7582 else if ((field->type == MYSQL_TYPE_DOUBLE ||
7583 field->type == MYSQL_TYPE_FLOAT ) &&
7584 field->decimals >= 31)
7585 {
7586 /* Convert 1.2e+018 to 1.2e+18 and 1.2e-018 to 1.2e-18 */
7587 char *start= strchr(val, 'e');
7588 if (start && strlen(start) >= 5 &&
7589 (start[1] == '-' || start[1] == '+') && start[2] == '0')
7590 {
7591 start+=2; /* Now points at first '0' */
7592 if (field->flags & ZEROFILL_FLAG)
7593 {
7594 /* Move all chars before the first '0' one step right */
7595 memmove(val + 1, val, start - val);
7596 *val= '0';
7597 }
7598 else
7599 {
7600 /* Move all chars after the first '0' one step left */
7601 memmove(start, start + 1, strlen(start));
7602 len--;
7603 }
7604 }
7605 }
7606#endif
7607
7608 if (!display_result_vertically)
7609 {
7610 if (col_idx)
7611 dynstr_append_mem(ds, "\t", 1);
7612 replace_dynstr_append_mem(ds, val, len);
7613 }
7614 else
7615 {
7616 dynstr_append(ds, field->name);
7617 dynstr_append_mem(ds, "\t", 1);
7618 replace_dynstr_append_mem(ds, val, len);
7619 dynstr_append_mem(ds, "\n", 1);
7620 }
7621}
7622
7623
7624/*
7625 Append all results to the dynamic string separated with '\t'
7626 Values may be converted with 'replace_column'
7627*/
7628
7629void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
7630{
7631 MYSQL_ROW row;
7632 uint num_fields= mysql_num_fields(res);
7633 MYSQL_FIELD *fields= mysql_fetch_fields(res);
7634 ulong *lengths;
7635
7636 while ((row = mysql_fetch_row(res)))
7637 {
7638 uint i;
7639 lengths = mysql_fetch_lengths(res);
7640 for (i = 0; i < num_fields; i++)
7641 append_field(ds, i, &fields[i],
7642 row[i], lengths[i], !row[i]);
7643 if (!display_result_vertically)
7644 dynstr_append_mem(ds, "\n", 1);
7645 }
7646}
7647
7648
7649/*
7650 Append all results from ps execution to the dynamic string separated
7651 with '\t'. Values may be converted with 'replace_column'
7652*/
7653
7654void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt,
7655 MYSQL_FIELD *fields, uint num_fields)
7656{
7657 MYSQL_BIND *my_bind;
7658 my_bool *is_null;
7659 ulong *length;
7660 uint i;
7661 int error;
7662
7663 /* Allocate array with bind structs, lengths and NULL flags */
7664 my_bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND),
7665 MYF(MY_WME | MY_FAE | MY_ZEROFILL));
7666 length= (ulong*) my_malloc(num_fields * sizeof(ulong),
7667 MYF(MY_WME | MY_FAE));
7668 is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool),
7669 MYF(MY_WME | MY_FAE));
7670
7671 /* Allocate data for the result of each field */
7672 for (i= 0; i < num_fields; i++)
7673 {
7674 uint max_length= fields[i].max_length + 1;
7675 my_bind[i].buffer_type= MYSQL_TYPE_STRING;
7676 my_bind[i].buffer= my_malloc(max_length, MYF(MY_WME | MY_FAE));
7677 my_bind[i].buffer_length= max_length;
7678 my_bind[i].is_null= &is_null[i];
7679 my_bind[i].length= &length[i];
7680
7681 DBUG_PRINT("bind", ("col[%d]: buffer_type: %d, buffer_length: %lu",
7682 i, my_bind[i].buffer_type, my_bind[i].buffer_length));
7683 }
7684
7685 if (mysql_stmt_bind_result(stmt, my_bind))
7686 die("mysql_stmt_bind_result failed: %d: %s",
7687 mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
7688
7689 while ((error=mysql_stmt_fetch(stmt)) == 0)
7690 {
7691 for (i= 0; i < num_fields; i++)
7692 append_field(ds, i, &fields[i], (char*)my_bind[i].buffer,
7693 *my_bind[i].length, *my_bind[i].is_null);
7694 if (!display_result_vertically)
7695 dynstr_append_mem(ds, "\n", 1);
7696 }
7697
7698 if (error != MYSQL_NO_DATA)
7699 die("mysql_fetch didn't end with MYSQL_NO_DATA from statement: "
7700 "error: %d", error);
7701 if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
7702 die("mysql_fetch didn't end with MYSQL_NO_DATA from statement: %d %s",
7703 mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
7704
7705 for (i= 0; i < num_fields; i++)
7706 {
7707 /* Free data for output */
7708 my_free(my_bind[i].buffer);
7709 }
7710 /* Free array with bind structs, lengths and NULL flags */
7711 my_free(my_bind);
7712 my_free(length);
7713 my_free(is_null);
7714}
7715
7716
7717/*
7718 Append metadata for fields to output
7719*/
7720
7721void append_metadata(DYNAMIC_STRING *ds,
7722 MYSQL_FIELD *field,
7723 uint num_fields)
7724{
7725 MYSQL_FIELD *field_end;
7726 dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t"
7727 "Column_alias\tType\tLength\tMax length\tIs_null\t"
7728 "Flags\tDecimals\tCharsetnr\n");
7729
7730 for (field_end= field+num_fields ;
7731 field < field_end ;
7732 field++)
7733 {
7734 dynstr_append_mem(ds, field->catalog,
7735 field->catalog_length);
7736 dynstr_append_mem(ds, "\t", 1);
7737 dynstr_append_mem(ds, field->db, field->db_length);
7738 dynstr_append_mem(ds, "\t", 1);
7739 dynstr_append_mem(ds, field->org_table,
7740 field->org_table_length);
7741 dynstr_append_mem(ds, "\t", 1);
7742 dynstr_append_mem(ds, field->table,
7743 field->table_length);
7744 dynstr_append_mem(ds, "\t", 1);
7745 dynstr_append_mem(ds, field->org_name,
7746 field->org_name_length);
7747 dynstr_append_mem(ds, "\t", 1);
7748 dynstr_append_mem(ds, field->name, field->name_length);
7749 dynstr_append_mem(ds, "\t", 1);
7750 replace_dynstr_append_uint(ds, field->type);
7751 dynstr_append_mem(ds, "\t", 1);
7752 replace_dynstr_append_uint(ds, field->length);
7753 dynstr_append_mem(ds, "\t", 1);
7754 replace_dynstr_append_uint(ds, field->max_length);
7755 dynstr_append_mem(ds, "\t", 1);
7756 dynstr_append_mem(ds, (IS_NOT_NULL(field->flags) ? "N" : "Y"), 1);
7757 dynstr_append_mem(ds, "\t", 1);
7758 replace_dynstr_append_uint(ds, field->flags);
7759 dynstr_append_mem(ds, "\t", 1);
7760 replace_dynstr_append_uint(ds, field->decimals);
7761 dynstr_append_mem(ds, "\t", 1);
7762 replace_dynstr_append_uint(ds, field->charsetnr);
7763 dynstr_append_mem(ds, "\n", 1);
7764 }
7765}
7766
7767
7768/*
7769 Append affected row count and other info to output
7770*/
7771
7772void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows,
7773 const char *info)
7774{
7775 char buf[40], buff2[21];
7776 sprintf(buf,"affected rows: %s\n", llstr(affected_rows, buff2));
7777 dynstr_append(ds, buf);
7778 if (info)
7779 {
7780 dynstr_append(ds, "info: ");
7781 dynstr_append(ds, info);
7782 dynstr_append_mem(ds, "\n", 1);
7783 }
7784}
7785
7786
7787/*
7788 Display the table headings with the names tab separated
7789*/
7790
7791void append_table_headings(DYNAMIC_STRING *ds,
7792 MYSQL_FIELD *field,
7793 uint num_fields)
7794{
7795 uint col_idx;
7796 if (disable_column_names)
7797 return;
7798 for (col_idx= 0; col_idx < num_fields; col_idx++)
7799 {
7800 if (col_idx)
7801 dynstr_append_mem(ds, "\t", 1);
7802 replace_dynstr_append(ds, field[col_idx].name);
7803 }
7804 dynstr_append_mem(ds, "\n", 1);
7805}
7806
7807/*
7808 Fetch warnings from server and append to ds
7809
7810 RETURN VALUE
7811 Number of warnings appended to ds
7812*/
7813
7814int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql)
7815{
7816 uint count;
7817 MYSQL_RES *warn_res;
7818 DYNAMIC_STRING res;
7819 DBUG_ENTER("append_warnings");
7820
7821 if (!(count= mysql_warning_count(mysql)))
7822 DBUG_RETURN(0);
7823
7824 /*
7825 If one day we will support execution of multi-statements
7826 through PS API we should not issue SHOW WARNINGS until
7827 we have not read all results...
7828 */
7829 DBUG_ASSERT(!mysql_more_results(mysql));
7830
7831 if (mysql_real_query(mysql, "SHOW WARNINGS", 13))
7832 die("Error running query \"SHOW WARNINGS\": %s", mysql_error(mysql));
7833
7834 if (!(warn_res= mysql_store_result(mysql)))
7835 die("Warning count is %u but didn't get any warnings",
7836 count);
7837
7838 init_dynamic_string(&res, "", 1024, 1024);
7839
7840 append_result(&res, warn_res);
7841 mysql_free_result(warn_res);
7842
7843 DBUG_PRINT("warnings", ("%s", res.str));
7844
7845 if (display_result_sorted)
7846 dynstr_append_sorted(ds, &res, 0);
7847 else
7848 dynstr_append_mem(ds, res.str, res.length);
7849 dynstr_free(&res);
7850 DBUG_RETURN(count);
7851}
7852
7853
7854/*
7855 Handle situation where query is sent but there is no active connection
7856 (e.g directly after disconnect).
7857
7858 We emulate MySQL-compatible behaviour of sending something on a closed
7859 connection.
7860*/
7861static void handle_no_active_connection(struct st_command *command,
7862 struct st_connection *cn, DYNAMIC_STRING *ds)
7863{
7864 handle_error(command, 2006, "MySQL server has gone away", "000000", ds);
7865 cn->pending= FALSE;
7866 var_set_errno(2006);
7867}
7868
7869
7870/*
7871 Run query using MySQL C API
7872
7873 SYNOPSIS
7874 run_query_normal()
7875 mysql mysql handle
7876 command current command pointer
7877 flags flags indicating if we should SEND and/or REAP
7878 query query string to execute
7879 query_len length query string to execute
7880 ds output buffer where to store result form query
7881*/
7882
7883void run_query_normal(struct st_connection *cn, struct st_command *command,
7884 int flags, char *query, size_t query_len,
7885 DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings)
7886{
7887 MYSQL_RES *res= 0;
7888 MYSQL *mysql= cn->mysql;
7889 int err= 0, counter= 0;
7890 DBUG_ENTER("run_query_normal");
7891 DBUG_PRINT("enter",("flags: %d", flags));
7892 DBUG_PRINT("enter", ("query: '%-.60s'", query));
7893
7894 if (!mysql)
7895 {
7896 handle_no_active_connection(command, cn, ds);
7897 DBUG_VOID_RETURN;
7898 }
7899
7900 if (flags & QUERY_SEND_FLAG)
7901 {
7902 /*
7903 Send the query
7904 */
7905 if (do_send_query(cn, query, query_len))
7906 {
7907 handle_error(command, mysql_errno(mysql), mysql_error(mysql),
7908 mysql_sqlstate(mysql), ds);
7909 goto end;
7910 }
7911 }
7912 if (!(flags & QUERY_REAP_FLAG))
7913 {
7914 cn->pending= TRUE;
7915 DBUG_VOID_RETURN;
7916 }
7917
7918 do
7919 {
7920 /*
7921 When on first result set, call mysql_read_query_result to retrieve
7922 answer to the query sent earlier
7923 */
7924 if ((counter==0) && do_read_query_result(cn))
7925 {
7926 /* we've failed to collect the result set */
7927 cn->pending= TRUE;
7928 handle_error(command, mysql_errno(mysql), mysql_error(mysql),
7929 mysql_sqlstate(mysql), ds);
7930 goto end;
7931
7932 }
7933
7934 /*
7935 Store the result of the query if it will return any fields
7936 */
7937 if (mysql_field_count(mysql) && ((res= mysql_store_result(mysql)) == 0))
7938 {
7939 handle_error(command, mysql_errno(mysql), mysql_error(mysql),
7940 mysql_sqlstate(mysql), ds);
7941 goto end;
7942 }
7943
7944 if (!disable_result_log)
7945 {
7946 if (res)
7947 {
7948 MYSQL_FIELD *fields= mysql_fetch_fields(res);
7949 uint num_fields= mysql_num_fields(res);
7950
7951 if (display_metadata)
7952 append_metadata(ds, fields, num_fields);
7953
7954 if (!display_result_vertically)
7955 append_table_headings(ds, fields, num_fields);
7956
7957 append_result(ds, res);
7958 }
7959
7960 /*
7961 Need to call mysql_affected_rows() before the "new"
7962 query to find the warnings.
7963 */
7964 if (!disable_info)
7965 append_info(ds, mysql_affected_rows(mysql), mysql_info(mysql));
7966
7967 /*
7968 Add all warnings to the result. We can't do this if we are in
7969 the middle of processing results from multi-statement, because
7970 this will break protocol.
7971 */
7972 if (!disable_warnings && !mysql_more_results(mysql))
7973 {
7974 if (append_warnings(ds_warnings, mysql) || ds_warnings->length)
7975 {
7976 dynstr_append_mem(ds, "Warnings:\n", 10);
7977 dynstr_append_mem(ds, ds_warnings->str, ds_warnings->length);
7978 }
7979 }
7980 }
7981
7982 if (res)
7983 {
7984 mysql_free_result(res);
7985 res= 0;
7986 }
7987 counter++;
7988 } while (!(err= mysql_next_result(mysql)));
7989 if (err > 0)
7990 {
7991 /* We got an error from mysql_next_result, maybe expected */
7992 handle_error(command, mysql_errno(mysql), mysql_error(mysql),
7993 mysql_sqlstate(mysql), ds);
7994 goto end;
7995 }
7996 DBUG_ASSERT(err == -1); /* Successful and there are no more results */
7997
7998 /* If we come here the query is both executed and read successfully */
7999 handle_no_error(command);
8000 revert_properties();
8001
8002end:
8003
8004 cn->pending= FALSE;
8005 /*
8006 We save the return code (mysql_errno(mysql)) from the last call sent
8007 to the server into the mysqltest builtin variable $mysql_errno. This
8008 variable then can be used from the test case itself.
8009 */
8010 var_set_errno(mysql_errno(mysql));
8011 DBUG_VOID_RETURN;
8012}
8013
8014
8015/*
8016 Check whether given error is in list of expected errors
8017
8018 SYNOPSIS
8019 match_expected_error()
8020
8021 PARAMETERS
8022 command the current command (and its expect-list)
8023 err_errno error number of the error that actually occurred
8024 err_sqlstate SQL-state that was thrown, or NULL for impossible
8025 (file-ops, diff, etc.)
8026
8027 RETURNS
8028 -1 for not in list, index in list of expected errors otherwise
8029
8030 NOTE
8031 If caller needs to know whether the list was empty, they should
8032 check command->expected_errors.count.
8033*/
8034
8035static int match_expected_error(struct st_command *command,
8036 unsigned int err_errno,
8037 const char *err_sqlstate)
8038{
8039 uint i;
8040
8041 for (i= 0 ; (uint) i < command->expected_errors.count ; i++)
8042 {
8043 if ((command->expected_errors.err[i].type == ERR_ERRNO) &&
8044 (command->expected_errors.err[i].code.errnum == err_errno))
8045 return i;
8046
8047 if (command->expected_errors.err[i].type == ERR_SQLSTATE)
8048 {
8049 /*
8050 NULL is quite likely, but not in conjunction with a SQL-state expect!
8051 */
8052 if (unlikely(err_sqlstate == NULL))
8053 die("expecting a SQL-state (%s) from query '%s' which cannot "
8054 "produce one...",
8055 command->expected_errors.err[i].code.sqlstate, command->query);
8056
8057 if (strncmp(command->expected_errors.err[i].code.sqlstate,
8058 err_sqlstate, SQLSTATE_LENGTH) == 0)
8059 return i;
8060 }
8061 }
8062 return -1;
8063}
8064
8065
8066/*
8067 Handle errors which occurred during execution
8068
8069 SYNOPSIS
8070 handle_error()
8071 q - query context
8072 err_errno - error number
8073 err_error - error message
8074 err_sqlstate - sql state
8075 ds - dynamic string which is used for output buffer
8076
8077 NOTE
8078 If there is an unexpected error this function will abort mysqltest
8079 immediately.
8080*/
8081
8082void handle_error(struct st_command *command,
8083 unsigned int err_errno, const char *err_error,
8084 const char *err_sqlstate, DYNAMIC_STRING *ds)
8085{
8086 int i;
8087
8088 DBUG_ENTER("handle_error");
8089
8090 command->used_replace= 1;
8091 if (command->require_file)
8092 {
8093 /*
8094 The query after a "--require" failed. This is fine as long the server
8095 returned a valid response. Don't allow 2013 or 2006 to trigger an
8096 abort_not_supported_test
8097 */
8098 if (err_errno == CR_SERVER_LOST ||
8099 err_errno == CR_SERVER_GONE_ERROR)
8100 die("require query '%s' failed: %d: %s", command->query,
8101 err_errno, err_error);
8102
8103 /* Abort the run of this test, pass the failed query as reason */
8104 abort_not_supported_test("Query '%s' failed, required functionality " \
8105 "not supported", command->query);
8106 }
8107
8108 if (command->abort_on_error)
8109 {
8110 report_or_die("query '%s' failed: %d: %s", command->query, err_errno,
8111 err_error);
8112 DBUG_VOID_RETURN;
8113 }
8114
8115 DBUG_PRINT("info", ("expected_errors.count: %d",
8116 command->expected_errors.count));
8117
8118 i= match_expected_error(command, err_errno, err_sqlstate);
8119
8120 if (i >= 0)
8121 {
8122 if (!disable_result_log)
8123 {
8124 if (command->expected_errors.count == 1)
8125 {
8126 /* Only log error if there is one possible error */
8127 dynstr_append_mem(ds, "ERROR ", 6);
8128 replace_dynstr_append(ds, err_sqlstate);
8129 dynstr_append_mem(ds, ": ", 2);
8130 replace_dynstr_append(ds, err_error);
8131 dynstr_append_mem(ds,"\n",1);
8132 }
8133 /* Don't log error if we may not get an error */
8134 else if (command->expected_errors.err[0].type == ERR_SQLSTATE ||
8135 (command->expected_errors.err[0].type == ERR_ERRNO &&
8136 command->expected_errors.err[0].code.errnum != 0))
8137 dynstr_append(ds,"Got one of the listed errors\n");
8138 }
8139 /* OK */
8140 revert_properties();
8141 DBUG_VOID_RETURN;
8142 }
8143
8144 DBUG_PRINT("info",("i: %d expected_errors: %d", i,
8145 command->expected_errors.count));
8146
8147 if (!disable_result_log)
8148 {
8149 dynstr_append_mem(ds, "ERROR ",6);
8150 replace_dynstr_append(ds, err_sqlstate);
8151 dynstr_append_mem(ds, ": ", 2);
8152 replace_dynstr_append(ds, err_error);
8153 dynstr_append_mem(ds, "\n", 1);
8154 }
8155
8156 if (command->expected_errors.count > 0)
8157 {
8158 if (command->expected_errors.err[0].type == ERR_ERRNO)
8159 report_or_die("query '%s' failed with wrong errno %d: '%s', instead of "
8160 "%d...",
8161 command->query, err_errno, err_error,
8162 command->expected_errors.err[0].code.errnum);
8163 else
8164 report_or_die("query '%s' failed with wrong sqlstate %s: '%s', "
8165 "instead of %s...",
8166 command->query, err_sqlstate, err_error,
8167 command->expected_errors.err[0].code.sqlstate);
8168 }
8169
8170 revert_properties();
8171 DBUG_VOID_RETURN;
8172}
8173
8174
8175/*
8176 Handle absence of errors after execution
8177
8178 SYNOPSIS
8179 handle_no_error()
8180 q - context of query
8181
8182 RETURN VALUE
8183 error - function will not return
8184*/
8185
8186void handle_no_error(struct st_command *command)
8187{
8188 DBUG_ENTER("handle_no_error");
8189
8190 if (command->expected_errors.err[0].type == ERR_ERRNO &&
8191 command->expected_errors.err[0].code.errnum != 0)
8192 {
8193 /* Error code we wanted was != 0, i.e. not an expected success */
8194 report_or_die("query '%s' succeeded - should have failed with errno %d...",
8195 command->query, command->expected_errors.err[0].code.errnum);
8196 }
8197 else if (command->expected_errors.err[0].type == ERR_SQLSTATE &&
8198 strcmp(command->expected_errors.err[0].code.sqlstate,"00000") != 0)
8199 {
8200 /* SQLSTATE we wanted was != "00000", i.e. not an expected success */
8201 report_or_die("query '%s' succeeded - should have failed with "
8202 "sqlstate %s...",
8203 command->query,
8204 command->expected_errors.err[0].code.sqlstate);
8205 }
8206 DBUG_VOID_RETURN;
8207}
8208
8209
8210/*
8211 Run query using prepared statement C API
8212
8213 SYNPOSIS
8214 run_query_stmt
8215 mysql - mysql handle
8216 command - currrent command pointer
8217 query - query string to execute
8218 query_len - length query string to execute
8219 ds - output buffer where to store result form query
8220
8221 RETURN VALUE
8222 error - function will not return
8223*/
8224
8225void run_query_stmt(struct st_connection *cn, struct st_command *command,
8226 char *query, size_t query_len, DYNAMIC_STRING *ds,
8227 DYNAMIC_STRING *ds_warnings)
8228{
8229 MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */
8230 MYSQL *mysql= cn->mysql;
8231 MYSQL_STMT *stmt;
8232 DYNAMIC_STRING ds_prepare_warnings;
8233 DYNAMIC_STRING ds_execute_warnings;
8234 DBUG_ENTER("run_query_stmt");
8235 DBUG_PRINT("query", ("'%-.60s'", query));
8236
8237 /*
8238 Init a new stmt if it's not already one created for this connection
8239 */
8240 if(!(stmt= cn->stmt))
8241 {
8242 if (!(stmt= mysql_stmt_init(mysql)))
8243 die("unable to init stmt structure");
8244 cn->stmt= stmt;
8245 }
8246
8247 /* Init dynamic strings for warnings */
8248 if (!disable_warnings)
8249 {
8250 init_dynamic_string(&ds_prepare_warnings, NULL, 0, 256);
8251 init_dynamic_string(&ds_execute_warnings, NULL, 0, 256);
8252 }
8253
8254 /*
8255 Prepare the query
8256 */
8257 if (do_stmt_prepare(cn, query, query_len))
8258 {
8259 handle_error(command, mysql_stmt_errno(stmt),
8260 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
8261 goto end;
8262 }
8263
8264 /*
8265 Get the warnings from mysql_stmt_prepare and keep them in a
8266 separate string
8267 */
8268 if (!disable_warnings)
8269 append_warnings(&ds_prepare_warnings, mysql);
8270
8271 /*
8272 No need to call mysql_stmt_bind_param() because we have no
8273 parameter markers.
8274 */
8275
8276#if MYSQL_VERSION_ID >= 50000
8277 if (cursor_protocol_enabled)
8278 {
8279 /*
8280 Use cursor when retrieving result
8281 */
8282 ulong type= CURSOR_TYPE_READ_ONLY;
8283 if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type))
8284 die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s",
8285 mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
8286 }
8287#endif
8288
8289 /*
8290 Execute the query
8291 */
8292 if (do_stmt_execute(cn))
8293 {
8294 handle_error(command, mysql_stmt_errno(stmt),
8295 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
8296 goto end;
8297 }
8298
8299 /*
8300 When running in cursor_protocol get the warnings from execute here
8301 and keep them in a separate string for later.
8302 */
8303 if (cursor_protocol_enabled && !disable_warnings)
8304 append_warnings(&ds_execute_warnings, mysql);
8305
8306 /*
8307 We instruct that we want to update the "max_length" field in
8308 mysql_stmt_store_result(), this is our only way to know how much
8309 buffer to allocate for result data
8310 */
8311 {
8312 my_bool one= 1;
8313 if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one))
8314 die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s",
8315 mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
8316 }
8317
8318 /*
8319 If we got here the statement succeeded and was expected to do so,
8320 get data. Note that this can still give errors found during execution!
8321 Store the result of the query if if will return any fields
8322 */
8323 if (mysql_stmt_field_count(stmt) && mysql_stmt_store_result(stmt))
8324 {
8325 handle_error(command, mysql_stmt_errno(stmt),
8326 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
8327 goto end;
8328 }
8329
8330 /* If we got here the statement was both executed and read successfully */
8331 handle_no_error(command);
8332 if (!disable_result_log)
8333 {
8334 /*
8335 Not all statements creates a result set. If there is one we can
8336 now create another normal result set that contains the meta
8337 data. This set can be handled almost like any other non prepared
8338 statement result set.
8339 */
8340 if ((res= mysql_stmt_result_metadata(stmt)) != NULL)
8341 {
8342 /* Take the column count from meta info */
8343 MYSQL_FIELD *fields= mysql_fetch_fields(res);
8344 uint num_fields= mysql_num_fields(res);
8345
8346 if (display_metadata)
8347 append_metadata(ds, fields, num_fields);
8348
8349 if (!display_result_vertically)
8350 append_table_headings(ds, fields, num_fields);
8351
8352 append_stmt_result(ds, stmt, fields, num_fields);
8353
8354 mysql_free_result(res); /* Free normal result set with meta data */
8355
8356 /*
8357 Normally, if there is a result set, we do not show warnings from the
8358 prepare phase. This is because some warnings are generated both during
8359 prepare and execute; this would generate different warning output
8360 between normal and ps-protocol test runs.
8361
8362 The --enable_prepare_warnings command can be used to change this so
8363 that warnings from both the prepare and execute phase are shown.
8364 */
8365 if (!disable_warnings && !prepare_warnings_enabled)
8366 dynstr_set(&ds_prepare_warnings, NULL);
8367 }
8368 else
8369 {
8370 /*
8371 This is a query without resultset
8372 */
8373 }
8374
8375 /*
8376 Fetch info before fetching warnings, since it will be reset
8377 otherwise.
8378 */
8379 if (!disable_info)
8380 append_info(ds, mysql_stmt_affected_rows(stmt), mysql_info(mysql));
8381
8382 if (!disable_warnings)
8383 {
8384 /* Get the warnings from execute */
8385
8386 /* Append warnings to ds - if there are any */
8387 if (append_warnings(&ds_execute_warnings, mysql) ||
8388 ds_execute_warnings.length ||
8389 ds_prepare_warnings.length ||
8390 ds_warnings->length)
8391 {
8392 dynstr_append_mem(ds, "Warnings:\n", 10);
8393 if (ds_warnings->length)
8394 dynstr_append_mem(ds, ds_warnings->str,
8395 ds_warnings->length);
8396 if (ds_prepare_warnings.length)
8397 dynstr_append_mem(ds, ds_prepare_warnings.str,
8398 ds_prepare_warnings.length);
8399 if (ds_execute_warnings.length)
8400 dynstr_append_mem(ds, ds_execute_warnings.str,
8401 ds_execute_warnings.length);
8402 }
8403 }
8404 }
8405
8406end:
8407 if (!disable_warnings)
8408 {
8409 dynstr_free(&ds_prepare_warnings);
8410 dynstr_free(&ds_execute_warnings);
8411 }
8412
8413 /*
8414 We save the return code (mysql_stmt_errno(stmt)) from the last call sent
8415 to the server into the mysqltest builtin variable $mysql_errno. This
8416 variable then can be used from the test case itself.
8417 */
8418
8419 var_set_errno(mysql_stmt_errno(stmt));
8420
8421 revert_properties();
8422
8423 /* Close the statement if reconnect, need new prepare */
8424 {
8425#ifndef EMBEDDED_LIBRARY
8426 my_bool reconnect;
8427 mysql_get_option(mysql, MYSQL_OPT_RECONNECT, &reconnect);
8428 if (reconnect)
8429#else
8430 if (mysql->reconnect)
8431#endif
8432 {
8433 mysql_stmt_close(stmt);
8434 cn->stmt= NULL;
8435 }
8436 }
8437
8438 DBUG_VOID_RETURN;
8439}
8440
8441
8442
8443/*
8444 Create a util connection if one does not already exists
8445 and use that to run the query
8446 This is done to avoid implict commit when creating/dropping objects such
8447 as view, sp etc.
8448*/
8449
8450int util_query(MYSQL* org_mysql, const char* query){
8451
8452 MYSQL* mysql;
8453 DBUG_ENTER("util_query");
8454
8455 if(!(mysql= cur_con->util_mysql))
8456 {
8457 DBUG_PRINT("info", ("Creating util_mysql"));
8458 if (!(mysql= mysql_init(mysql)))
8459 die("Failed in mysql_init()");
8460
8461 if (opt_connect_timeout)
8462 mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT,
8463 (void *) &opt_connect_timeout);
8464
8465 /* enable local infile, in non-binary builds often disabled by default */
8466 mysql_options(mysql, MYSQL_OPT_LOCAL_INFILE, 0);
8467 mysql_options(mysql, MYSQL_OPT_NONBLOCK, 0);
8468 safe_connect(mysql, "util", org_mysql->host, org_mysql->user,
8469 org_mysql->passwd, org_mysql->db, org_mysql->port,
8470 org_mysql->unix_socket);
8471
8472 cur_con->util_mysql= mysql;
8473 }
8474
8475 int ret= mysql_query(mysql, query);
8476 DBUG_RETURN(ret);
8477}
8478
8479
8480
8481/*
8482 Run query
8483
8484 SYNPOSIS
8485 run_query()
8486 mysql mysql handle
8487 command currrent command pointer
8488
8489 flags control the phased/stages of query execution to be performed
8490 if QUERY_SEND_FLAG bit is on, the query will be sent. If QUERY_REAP_FLAG
8491 is on the result will be read - for regular query, both bits must be on
8492*/
8493
8494void run_query(struct st_connection *cn, struct st_command *command, int flags)
8495{
8496 MYSQL *mysql= cn->mysql;
8497 DYNAMIC_STRING *ds;
8498 DYNAMIC_STRING *save_ds= NULL;
8499 DYNAMIC_STRING ds_result;
8500 DYNAMIC_STRING ds_sorted;
8501 DYNAMIC_STRING ds_warnings;
8502 char *query;
8503 size_t query_len;
8504 my_bool view_created= 0, sp_created= 0;
8505 my_bool complete_query= ((flags & QUERY_SEND_FLAG) &&
8506 (flags & QUERY_REAP_FLAG));
8507 DBUG_ENTER("run_query");
8508
8509 if (cn->pending && (flags & QUERY_SEND_FLAG))
8510 die("Cannot run query on connection between send and reap");
8511
8512 if (!(flags & QUERY_SEND_FLAG) && !cn->pending)
8513 die("Cannot reap on a connection without pending send");
8514
8515 init_dynamic_string(&ds_warnings, NULL, 0, 256);
8516 ds_warn= &ds_warnings;
8517
8518 /*
8519 Evaluate query if this is an eval command
8520 */
8521 if (command->type == Q_EVAL || command->type == Q_SEND_EVAL ||
8522 command->type == Q_EVALP)
8523 {
8524 if (!command->eval_query.str)
8525 init_dynamic_string(&command->eval_query, "", command->query_len + 256,
8526 1024);
8527 else
8528 dynstr_set(&command->eval_query, 0);
8529 do_eval(&command->eval_query, command->query, command->end, FALSE);
8530 query= command->eval_query.str;
8531 query_len= command->eval_query.length;
8532 }
8533 else
8534 {
8535 query = command->query;
8536 query_len = strlen(query);
8537 }
8538
8539 /*
8540 When command->require_file is set the output of _this_ query
8541 should be compared with an already existing file
8542 Create a temporary dynamic string to contain the output from
8543 this query.
8544 */
8545 if (command->require_file)
8546 {
8547 init_dynamic_string(&ds_result, "", 1024, 1024);
8548 ds= &ds_result;
8549 }
8550 else
8551 ds= &ds_res;
8552
8553 /*
8554 Log the query into the output buffer
8555 */
8556 if (!disable_query_log && (flags & QUERY_SEND_FLAG))
8557 {
8558 char *print_query= query;
8559 size_t print_len= query_len;
8560 if (flags & QUERY_PRINT_ORIGINAL_FLAG)
8561 {
8562 print_query= command->query;
8563 print_len= (int)(command->end - command->query);
8564 }
8565 replace_dynstr_append_mem(ds, print_query, print_len);
8566 dynstr_append_mem(ds, delimiter, delimiter_length);
8567 dynstr_append_mem(ds, "\n", 1);
8568 }
8569
8570 /* We're done with this flag */
8571 flags &= ~QUERY_PRINT_ORIGINAL_FLAG;
8572
8573 /*
8574 Write the command to the result file before we execute the query
8575 This is needed to be able to analyse the log if something goes
8576 wrong
8577 */
8578 log_file.write(&ds_res);
8579 log_file.flush();
8580 dynstr_set(&ds_res, 0);
8581
8582 if (view_protocol_enabled &&
8583 complete_query &&
8584 match_re(&view_re, query))
8585 {
8586 /*
8587 Create the query as a view.
8588 Use replace since view can exist from a failed mysqltest run
8589 */
8590 DYNAMIC_STRING query_str;
8591 init_dynamic_string(&query_str,
8592 "CREATE OR REPLACE VIEW mysqltest_tmp_v AS ",
8593 query_len+64, 256);
8594 dynstr_append_mem(&query_str, query, query_len);
8595 if (util_query(mysql, query_str.str))
8596 {
8597 /*
8598 Failed to create the view, this is not fatal
8599 just run the query the normal way
8600 */
8601 DBUG_PRINT("view_create_error",
8602 ("Failed to create view '%s': %d: %s", query_str.str,
8603 mysql_errno(mysql), mysql_error(mysql)));
8604
8605 /* Log error to create view */
8606 verbose_msg("Failed to create view '%s' %d: %s", query_str.str,
8607 mysql_errno(mysql), mysql_error(mysql));
8608 }
8609 else
8610 {
8611 /*
8612 Yes, it was possible to create this query as a view
8613 */
8614 view_created= 1;
8615 query= const_cast<char*>("SELECT * FROM mysqltest_tmp_v");
8616 query_len = strlen(query);
8617
8618 /*
8619 Collect warnings from create of the view that should otherwise
8620 have been produced when the SELECT was executed
8621 */
8622 append_warnings(&ds_warnings, cur_con->util_mysql);
8623 }
8624
8625 dynstr_free(&query_str);
8626 }
8627
8628 if (sp_protocol_enabled &&
8629 complete_query &&
8630 match_re(&sp_re, query))
8631 {
8632 /*
8633 Create the query as a stored procedure
8634 Drop first since sp can exist from a failed mysqltest run
8635 */
8636 DYNAMIC_STRING query_str;
8637 init_dynamic_string(&query_str,
8638 "DROP PROCEDURE IF EXISTS mysqltest_tmp_sp;",
8639 query_len+64, 256);
8640 util_query(mysql, query_str.str);
8641 dynstr_set(&query_str, "CREATE PROCEDURE mysqltest_tmp_sp()\n");
8642 dynstr_append_mem(&query_str, query, query_len);
8643 if (util_query(mysql, query_str.str))
8644 {
8645 /*
8646 Failed to create the stored procedure for this query,
8647 this is not fatal just run the query the normal way
8648 */
8649 DBUG_PRINT("sp_create_error",
8650 ("Failed to create sp '%s': %d: %s", query_str.str,
8651 mysql_errno(mysql), mysql_error(mysql)));
8652
8653 /* Log error to create sp */
8654 verbose_msg("Failed to create sp '%s' %d: %s", query_str.str,
8655 mysql_errno(mysql), mysql_error(mysql));
8656
8657 }
8658 else
8659 {
8660 sp_created= 1;
8661
8662 query= const_cast<char*>("CALL mysqltest_tmp_sp()");
8663 query_len = strlen(query);
8664 }
8665 dynstr_free(&query_str);
8666 }
8667
8668 if (display_result_sorted)
8669 {
8670 /*
8671 Collect the query output in a separate string
8672 that can be sorted before it's added to the
8673 global result string
8674 */
8675 init_dynamic_string(&ds_sorted, "", 1024, 1024);
8676 save_ds= ds; /* Remember original ds */
8677 ds= &ds_sorted;
8678 }
8679
8680 /*
8681 Find out how to run this query
8682
8683 Always run with normal C API if it's not a complete
8684 SEND + REAP
8685
8686 If it is a '?' in the query it may be a SQL level prepared
8687 statement already and we can't do it twice
8688 */
8689 if (ps_protocol_enabled &&
8690 complete_query &&
8691 match_re(&ps_re, query))
8692 run_query_stmt(cn, command, query, query_len, ds, &ds_warnings);
8693 else
8694 run_query_normal(cn, command, flags, query, query_len,
8695 ds, &ds_warnings);
8696
8697 dynstr_free(&ds_warnings);
8698 ds_warn= 0;
8699
8700 if (display_result_sorted)
8701 {
8702 /* Sort the result set and append it to result */
8703 dynstr_append_sorted(save_ds, &ds_sorted, 1);
8704 ds= save_ds;
8705 dynstr_free(&ds_sorted);
8706 }
8707
8708 if (sp_created)
8709 {
8710 if (util_query(mysql, "DROP PROCEDURE mysqltest_tmp_sp "))
8711 report_or_die("Failed to drop sp: %d: %s", mysql_errno(mysql),
8712 mysql_error(mysql));
8713 }
8714
8715 if (view_created)
8716 {
8717 if (util_query(mysql, "DROP VIEW mysqltest_tmp_v "))
8718 report_or_die("Failed to drop view: %d: %s",
8719 mysql_errno(mysql), mysql_error(mysql));
8720 }
8721
8722 if (command->require_file)
8723 {
8724 /* A result file was specified for _this_ query
8725 and the output should be checked against an already
8726 existing file which has been specified using --require or --result
8727 */
8728 check_require(ds, command->require_file);
8729 }
8730
8731 if (ds == &ds_result)
8732 dynstr_free(&ds_result);
8733 DBUG_VOID_RETURN;
8734}
8735
8736/****************************************************************************/
8737/*
8738 Functions to detect different SQL statements
8739*/
8740
8741char *re_eprint(int err)
8742{
8743 static char epbuf[100];
8744 size_t len __attribute__((unused))=
8745 regerror(err, (regex_t *)NULL, epbuf, sizeof(epbuf));
8746 assert(len <= sizeof(epbuf));
8747 return(epbuf);
8748}
8749
8750void init_re_comp(regex_t *re, const char* str)
8751{
8752 int err= regcomp(re, str, (REG_EXTENDED | REG_ICASE | REG_NOSUB | REG_DOTALL));
8753 if (err)
8754 {
8755 char erbuf[100];
8756 size_t len= regerror(err, re, erbuf, sizeof(erbuf));
8757 die("error %s, %d/%d `%s'\n",
8758 re_eprint(err), (int)len, (int)sizeof(erbuf), erbuf);
8759 }
8760}
8761
8762void init_re(void)
8763{
8764 /*
8765 Filter for queries that can be run using the
8766 MySQL Prepared Statements C API
8767 */
8768 const char *ps_re_str =
8769 "^("
8770 "[[:space:]]*REPLACE[[:space:]]|"
8771 "[[:space:]]*INSERT[[:space:]]|"
8772 "[[:space:]]*UPDATE[[:space:]]|"
8773 "[[:space:]]*DELETE[[:space:]]|"
8774 "[[:space:]]*SELECT[[:space:]]|"
8775 "[[:space:]]*CREATE[[:space:]]+TABLE[[:space:]]|"
8776 "[[:space:]]*DO[[:space:]]|"
8777 "[[:space:]]*SET[[:space:]]+OPTION[[:space:]]|"
8778 "[[:space:]]*DELETE[[:space:]]+MULTI[[:space:]]|"
8779 "[[:space:]]*UPDATE[[:space:]]+MULTI[[:space:]]|"
8780 "[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])";
8781
8782 /*
8783 Filter for queries that can be run using the
8784 Stored procedures
8785 */
8786 const char *sp_re_str =ps_re_str;
8787
8788 /*
8789 Filter for queries that can be run as views
8790 */
8791 const char *view_re_str =
8792 "^("
8793 "[[:space:]]*SELECT[[:space:]])";
8794
8795 init_re_comp(&ps_re, ps_re_str);
8796 init_re_comp(&sp_re, sp_re_str);
8797 init_re_comp(&view_re, view_re_str);
8798}
8799
8800
8801int match_re(regex_t *re, char *str)
8802{
8803 while (my_isspace(charset_info, *str))
8804 str++;
8805 if (str[0] == '/' && str[1] == '*')
8806 {
8807 char *comm_end= strstr (str, "*/");
8808 if (! comm_end)
8809 die("Statement is unterminated comment");
8810 str= comm_end + 2;
8811 }
8812
8813 int err= regexec(re, str, (size_t)0, NULL, 0);
8814
8815 if (err == 0)
8816 return 1;
8817 else if (err == REG_NOMATCH)
8818 return 0;
8819
8820 {
8821 char erbuf[100];
8822 size_t len= regerror(err, re, erbuf, sizeof(erbuf));
8823 die("error %s, %d/%d `%s'\n",
8824 re_eprint(err), (int)len, (int)sizeof(erbuf), erbuf);
8825 }
8826 return 0;
8827}
8828
8829void free_re(void)
8830{
8831 regfree(&ps_re);
8832 regfree(&sp_re);
8833 regfree(&view_re);
8834}
8835
8836/****************************************************************************/
8837
8838void get_command_type(struct st_command* command)
8839{
8840 char save;
8841 uint type;
8842 DBUG_ENTER("get_command_type");
8843
8844 if (*command->query == '}')
8845 {
8846 command->type = Q_END_BLOCK;
8847 DBUG_VOID_RETURN;
8848 }
8849
8850 save= command->query[command->first_word_len];
8851 command->query[command->first_word_len]= 0;
8852 type= find_type(command->query, &command_typelib, FIND_TYPE_NO_PREFIX);
8853 command->query[command->first_word_len]= save;
8854 if (type > 0)
8855 {
8856 command->type=(enum enum_commands) type; /* Found command */
8857
8858 /*
8859 Look for case where "query" was explicitly specified to
8860 force command being sent to server
8861 */
8862 if (type == Q_QUERY)
8863 {
8864 /* Skip the "query" part */
8865 command->query= command->first_argument;
8866 }
8867 }
8868 else
8869 {
8870 /* No mysqltest command matched */
8871
8872 if (command->type != Q_COMMENT_WITH_COMMAND)
8873 {
8874 /* A query that will sent to mysqld */
8875 command->type= Q_QUERY;
8876 }
8877 else
8878 {
8879 /* -- "comment" that didn't contain a mysqltest command */
8880 report_or_die("Found line beginning with -- that didn't contain " \
8881 "a valid mysqltest command, check your syntax or " \
8882 "use # if you intended to write a comment");
8883 command->type= Q_COMMENT;
8884 }
8885 }
8886
8887 /* Set expected error on command */
8888 memcpy(&command->expected_errors, &saved_expected_errors,
8889 sizeof(saved_expected_errors));
8890 DBUG_PRINT("info", ("There are %d expected errors",
8891 command->expected_errors.count));
8892 DBUG_VOID_RETURN;
8893}
8894
8895
8896
8897/*
8898 Record how many milliseconds it took to execute the test file
8899 up until the current line and write it to .progress file
8900
8901*/
8902
8903void mark_progress(struct st_command* command __attribute__((unused)),
8904 int line)
8905{
8906 static ulonglong progress_start= 0; // < Beware
8907 DYNAMIC_STRING ds_progress;
8908
8909 char buf[32], *end;
8910 ulonglong timer= timer_now();
8911 if (!progress_start)
8912 progress_start= timer;
8913 timer-= progress_start;
8914
8915 if (init_dynamic_string(&ds_progress, "", 256, 256))
8916 die("Out of memory");
8917
8918 /* Milliseconds since start */
8919 end= longlong10_to_str(timer, buf, 10);
8920 dynstr_append_mem(&ds_progress, buf, (int)(end-buf));
8921 dynstr_append_mem(&ds_progress, "\t", 1);
8922
8923 /* Parser line number */
8924 end= int10_to_str(line, buf, 10);
8925 dynstr_append_mem(&ds_progress, buf, (int)(end-buf));
8926 dynstr_append_mem(&ds_progress, "\t", 1);
8927
8928 /* Filename */
8929 dynstr_append(&ds_progress, cur_file->file_name);
8930 dynstr_append_mem(&ds_progress, ":", 1);
8931
8932 /* Line in file */
8933 end= int10_to_str(cur_file->lineno, buf, 10);
8934 dynstr_append_mem(&ds_progress, buf, (int)(end-buf));
8935
8936
8937 dynstr_append_mem(&ds_progress, "\n", 1);
8938
8939 progress_file.write(&ds_progress);
8940
8941 dynstr_free(&ds_progress);
8942
8943}
8944
8945#ifdef HAVE_STACKTRACE
8946
8947static void dump_backtrace(void)
8948{
8949 struct st_connection *conn= cur_con;
8950
8951 fprintf(stderr, "read_command_buf (%p): ", read_command_buf);
8952 my_safe_print_str(read_command_buf, sizeof(read_command_buf));
8953 fputc('\n', stderr);
8954
8955 if (conn)
8956 {
8957 fprintf(stderr, "conn->name (%p): ", conn->name);
8958 my_safe_print_str(conn->name, conn->name_len);
8959 fputc('\n', stderr);
8960#ifdef EMBEDDED_LIBRARY
8961 fprintf(stderr, "conn->cur_query (%p): ", conn->cur_query);
8962 my_safe_print_str(conn->cur_query, conn->cur_query_len);
8963 fputc('\n', stderr);
8964#endif
8965 }
8966 fputs("Attempting backtrace...\n", stderr);
8967 my_print_stacktrace(NULL, (ulong)my_thread_stack_size, 0);
8968}
8969
8970#else
8971
8972static void dump_backtrace(void)
8973{
8974 fputs("Backtrace not available.\n", stderr);
8975}
8976
8977#endif
8978
8979static sig_handler signal_handler(int sig)
8980{
8981 fprintf(stderr, "mysqltest got " SIGNAL_FMT "\n", sig);
8982 dump_backtrace();
8983
8984 fprintf(stderr, "Writing a core file...\n");
8985 fflush(stderr);
8986 my_write_core(sig);
8987#ifndef _WIN32
8988 exit(1); // Shouldn't get here but just in case
8989#endif
8990}
8991
8992#ifdef _WIN32
8993
8994LONG WINAPI exception_filter(EXCEPTION_POINTERS *exp)
8995{
8996 __try
8997 {
8998 my_set_exception_pointers(exp);
8999 signal_handler(exp->ExceptionRecord->ExceptionCode);
9000 }
9001 __except(EXCEPTION_EXECUTE_HANDLER)
9002 {
9003 fputs("Got exception in exception handler!\n", stderr);
9004 }
9005
9006 return EXCEPTION_CONTINUE_SEARCH;
9007}
9008
9009
9010static void init_signal_handling(void)
9011{
9012 UINT mode;
9013
9014 /* Set output destination of messages to the standard error stream. */
9015 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
9016 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
9017 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
9018 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
9019 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
9020 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
9021
9022 /* Do not not display the a error message box. */
9023 mode= SetErrorMode(0) | SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX;
9024 SetErrorMode(mode);
9025
9026 SetUnhandledExceptionFilter(exception_filter);
9027}
9028
9029#else /* _WIN32 */
9030
9031static void init_signal_handling(void)
9032{
9033 struct sigaction sa;
9034 DBUG_ENTER("init_signal_handling");
9035
9036#ifdef HAVE_STACKTRACE
9037 my_init_stacktrace();
9038#endif
9039
9040 sa.sa_flags = SA_RESETHAND | SA_NODEFER;
9041 sigemptyset(&sa.sa_mask);
9042 sigprocmask(SIG_SETMASK, &sa.sa_mask, NULL);
9043
9044 sa.sa_handler= signal_handler;
9045
9046 sigaction(SIGSEGV, &sa, NULL);
9047 sigaction(SIGABRT, &sa, NULL);
9048#ifdef SIGBUS
9049 sigaction(SIGBUS, &sa, NULL);
9050#endif
9051 sigaction(SIGILL, &sa, NULL);
9052 sigaction(SIGFPE, &sa, NULL);
9053 DBUG_VOID_RETURN;
9054}
9055
9056#endif /* !_WIN32 */
9057
9058int main(int argc, char **argv)
9059{
9060 struct st_command *command;
9061 my_bool q_send_flag= 0, abort_flag= 0;
9062 uint command_executed= 0, last_command_executed= 0;
9063 char save_file[FN_REFLEN];
9064 bool empty_result= FALSE;
9065 MY_INIT(argv[0]);
9066 DBUG_ENTER("main");
9067
9068 /* mysqltest has no way to free all its memory correctly */
9069 sf_leaking_memory= 1;
9070
9071 save_file[0]= 0;
9072 TMPDIR[0]= 0;
9073
9074 init_signal_handling();
9075
9076 /* Init expected errors */
9077 memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
9078
9079#ifdef EMBEDDED_LIBRARY
9080 /* set appropriate stack for the 'query' threads */
9081 (void) pthread_attr_init(&cn_thd_attrib);
9082 pthread_attr_setstacksize(&cn_thd_attrib, DEFAULT_THREAD_STACK);
9083#endif /*EMBEDDED_LIBRARY*/
9084
9085 /* Init file stack */
9086 memset(file_stack, 0, sizeof(file_stack));
9087 file_stack_end=
9088 file_stack + (sizeof(file_stack)/sizeof(struct st_test_file)) - 1;
9089 cur_file= file_stack;
9090
9091 /* Init block stack */
9092 memset(block_stack, 0, sizeof(block_stack));
9093 block_stack_end=
9094 block_stack + (sizeof(block_stack)/sizeof(struct st_block)) - 1;
9095 cur_block= block_stack;
9096 cur_block->ok= TRUE; /* Outer block should always be executed */
9097 cur_block->cmd= cmd_none;
9098
9099 my_init_dynamic_array(&q_lines, sizeof(struct st_command*), 1024, 1024, MYF(0));
9100
9101 if (my_hash_init2(&var_hash, 64, charset_info,
9102 128, 0, 0, get_var_key, 0, var_free, MYF(0)))
9103 die("Variable hash initialization failed");
9104
9105 {
9106 char path_separator[]= { FN_LIBCHAR, 0 };
9107 var_set_string("SYSTEM_PATH_SEPARATOR", path_separator);
9108 }
9109 var_set_string("MYSQL_SERVER_VERSION", MYSQL_SERVER_VERSION);
9110 var_set_string("MYSQL_SYSTEM_TYPE", SYSTEM_TYPE);
9111 var_set_string("MYSQL_MACHINE_TYPE", MACHINE_TYPE);
9112 if (sizeof(void *) == 8) {
9113 var_set_string("MYSQL_SYSTEM_ARCHITECTURE", "64");
9114 } else {
9115 var_set_string("MYSQL_SYSTEM_ARCHITECTURE", "32");
9116 }
9117
9118 memset(&master_pos, 0, sizeof(master_pos));
9119
9120 parser.current_line= parser.read_lines= 0;
9121 memset(&var_reg, 0, sizeof(var_reg));
9122
9123 init_builtin_echo();
9124#ifdef _WIN32
9125#ifndef USE_CYGWIN
9126 is_windows= 1;
9127#endif
9128 init_tmp_sh_file();
9129 init_win_path_patterns();
9130#endif
9131
9132 init_dynamic_string(&ds_res, "", 2048, 2048);
9133 init_alloc_root(&require_file_root, "require_file", 1024, 1024, MYF(0));
9134
9135 parse_args(argc, argv);
9136
9137 log_file.open(opt_logdir, result_file_name, ".log");
9138 verbose_msg("Logging to '%s'.", log_file.file_name());
9139 if (opt_mark_progress)
9140 {
9141 progress_file.open(opt_logdir, result_file_name, ".progress");
9142 verbose_msg("Tracing progress in '%s'.", progress_file.file_name());
9143 }
9144
9145 /* Init connections, allocate 1 extra as buffer + 1 for default */
9146 connections= (struct st_connection*)
9147 my_malloc((opt_max_connections+2) * sizeof(struct st_connection),
9148 MYF(MY_WME | MY_ZEROFILL));
9149 connections_end= connections + opt_max_connections +1;
9150 next_con= connections + 1;
9151
9152 var_set_int("$PS_PROTOCOL", ps_protocol);
9153 var_set_int("$NON_BLOCKING_API", non_blocking_api_enabled);
9154 var_set_int("$SP_PROTOCOL", sp_protocol);
9155 var_set_int("$VIEW_PROTOCOL", view_protocol);
9156 var_set_int("$CURSOR_PROTOCOL", cursor_protocol);
9157
9158 var_set_int("$ENABLED_QUERY_LOG", 1);
9159 var_set_int("$ENABLED_ABORT_ON_ERROR", 1);
9160 var_set_int("$ENABLED_RESULT_LOG", 1);
9161 var_set_int("$ENABLED_CONNECT_LOG", 0);
9162 var_set_int("$ENABLED_WARNINGS", 1);
9163 var_set_int("$ENABLED_INFO", 0);
9164 var_set_int("$ENABLED_METADATA", 0);
9165
9166 DBUG_PRINT("info",("result_file: '%s'",
9167 result_file_name ? result_file_name : ""));
9168 verbose_msg("Results saved in '%s'.",
9169 result_file_name ? result_file_name : "");
9170 if (mysql_server_init(embedded_server_arg_count,
9171 embedded_server_args,
9172 (char**) embedded_server_groups))
9173 die("Can't initialize MySQL server");
9174 server_initialized= 1;
9175 if (cur_file == file_stack && cur_file->file == 0)
9176 {
9177 cur_file->file= stdin;
9178 cur_file->file_name= my_strdup("<stdin>", MYF(MY_WME));
9179 cur_file->lineno= 1;
9180 }
9181 var_set_string("MYSQLTEST_FILE", cur_file->file_name);
9182 init_re();
9183
9184 /* Cursor protocol implies ps protocol */
9185 if (cursor_protocol)
9186 ps_protocol= 1;
9187
9188 ps_protocol_enabled= ps_protocol;
9189 sp_protocol_enabled= sp_protocol;
9190 view_protocol_enabled= view_protocol;
9191 cursor_protocol_enabled= cursor_protocol;
9192
9193 st_connection *con= connections;
9194 init_connection_thd(con);
9195 if (! (con->mysql= mysql_init(0)))
9196 die("Failed in mysql_init()");
9197 if (opt_connect_timeout)
9198 mysql_options(con->mysql, MYSQL_OPT_CONNECT_TIMEOUT,
9199 (void *) &opt_connect_timeout);
9200 if (opt_compress)
9201 mysql_options(con->mysql,MYSQL_OPT_COMPRESS,NullS);
9202 mysql_options(con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
9203 mysql_options(con->mysql, MYSQL_SET_CHARSET_NAME,
9204 charset_info->csname);
9205 if (opt_charsets_dir)
9206 mysql_options(con->mysql, MYSQL_SET_CHARSET_DIR,
9207 opt_charsets_dir);
9208
9209 if (opt_protocol)
9210 mysql_options(con->mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
9211
9212 if (opt_plugin_dir && *opt_plugin_dir)
9213 mysql_options(con->mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
9214
9215#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
9216
9217 if (opt_use_ssl)
9218 {
9219 mysql_ssl_set(con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
9220 opt_ssl_capath, opt_ssl_cipher);
9221 mysql_options(con->mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl);
9222 mysql_options(con->mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath);
9223#if MYSQL_VERSION_ID >= 50000
9224 /* Turn on ssl_verify_server_cert only if host is "localhost" */
9225 opt_ssl_verify_server_cert= opt_host && !strcmp(opt_host, "localhost");
9226 mysql_options(con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
9227 &opt_ssl_verify_server_cert);
9228#endif
9229 }
9230#endif
9231
9232#ifdef HAVE_SMEM
9233 if (shared_memory_base_name)
9234 mysql_options(con->mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name);
9235#endif
9236
9237 if (!(con->name = my_strdup("default", MYF(MY_WME))))
9238 die("Out of memory");
9239 mysql_options(con->mysql, MYSQL_OPT_NONBLOCK, 0);
9240
9241 safe_connect(con->mysql, con->name, opt_host, opt_user, opt_pass,
9242 opt_db, opt_port, unix_sock);
9243
9244 /* Use all time until exit if no explicit 'start_timer' */
9245 timer_start= timer_now();
9246
9247 /*
9248 Initialize $mysql_errno with -1, so we can
9249 - distinguish it from valid values ( >= 0 ) and
9250 - detect if there was never a command sent to the server
9251 */
9252 var_set_errno(-1);
9253
9254 set_current_connection(con);
9255
9256 if (opt_prologue)
9257 {
9258 open_file(opt_prologue);
9259 }
9260
9261 verbose_msg("Start processing test commands from '%s' ...", cur_file->file_name);
9262 while (!abort_flag && !read_command(&command))
9263 {
9264 my_bool ok_to_do;
9265 int current_line_inc = 1, processed = 0;
9266 if (command->type == Q_UNKNOWN || command->type == Q_COMMENT_WITH_COMMAND)
9267 get_command_type(command);
9268
9269 if (parsing_disabled &&
9270 command->type != Q_ENABLE_PARSING &&
9271 command->type != Q_DISABLE_PARSING)
9272 {
9273 /* Parsing is disabled, silently convert this line to a comment */
9274 command->type= Q_COMMENT;
9275 }
9276
9277 /* (Re-)set abort_on_error for this command */
9278 command->abort_on_error= (command->expected_errors.count == 0 &&
9279 abort_on_error);
9280
9281 /*
9282 some commands need to be executed or at least parsed unconditionally,
9283 because they change the grammar.
9284 */
9285 ok_to_do= cur_block->ok || command->type == Q_DELIMITER
9286 || command->type == Q_PERL;
9287 /*
9288 Some commands need to be "done" the first time if they may get
9289 re-iterated over in a true context. This can only happen if there's
9290 a while loop at some level above the current block.
9291 */
9292 if (!ok_to_do)
9293 {
9294 if (command->type == Q_SOURCE ||
9295 command->type == Q_ERROR ||
9296 command->type == Q_WRITE_FILE ||
9297 command->type == Q_APPEND_FILE)
9298 {
9299 for (struct st_block *stb= cur_block-1; stb >= block_stack; stb--)
9300 {
9301 if (stb->cmd == cmd_while)
9302 {
9303 ok_to_do= 1;
9304 break;
9305 }
9306 }
9307 }
9308 }
9309
9310 if (ok_to_do)
9311 {
9312 command->last_argument= command->first_argument;
9313 processed = 1;
9314 /* Need to remember this for handle_error() */
9315 curr_command= command;
9316 switch (command->type) {
9317 case Q_CONNECT:
9318 do_connect(command);
9319 break;
9320 case Q_CONNECTION: select_connection(command); break;
9321 case Q_DISCONNECT:
9322 case Q_DIRTY_CLOSE:
9323 do_close_connection(command); break;
9324 case Q_ENABLE_PREPARE_WARNINGS: prepare_warnings_enabled=1; break;
9325 case Q_DISABLE_PREPARE_WARNINGS: prepare_warnings_enabled=0; break;
9326 case Q_ENABLE_QUERY_LOG:
9327 set_property(command, P_QUERY, 0);
9328 break;
9329 case Q_DISABLE_QUERY_LOG:
9330 set_property(command, P_QUERY, 1);
9331 break;
9332 case Q_ENABLE_ABORT_ON_ERROR:
9333 set_property(command, P_ABORT, 1);
9334 break;
9335 case Q_DISABLE_ABORT_ON_ERROR:
9336 set_property(command, P_ABORT, 0);
9337 break;
9338 case Q_ENABLE_RESULT_LOG:
9339 set_property(command, P_RESULT, 0);
9340 break;
9341 case Q_DISABLE_RESULT_LOG:
9342 set_property(command, P_RESULT, 1);
9343 break;
9344 case Q_ENABLE_CONNECT_LOG:
9345 set_property(command, P_CONNECT, 0);
9346 break;
9347 case Q_DISABLE_CONNECT_LOG:
9348 set_property(command, P_CONNECT, 1);
9349 break;
9350 case Q_ENABLE_WARNINGS:
9351 set_property(command, P_WARN, 0);
9352 break;
9353 case Q_DISABLE_WARNINGS:
9354 set_property(command, P_WARN, 1);
9355 break;
9356 case Q_ENABLE_INFO:
9357 set_property(command, P_INFO, 0);
9358 break;
9359 case Q_DISABLE_INFO:
9360 set_property(command, P_INFO, 1);
9361 break;
9362 case Q_ENABLE_METADATA:
9363 set_property(command, P_META, 1);
9364 break;
9365 case Q_DISABLE_METADATA:
9366 set_property(command, P_META, 0);
9367 break;
9368 case Q_ENABLE_COLUMN_NAMES:
9369 disable_column_names= 0;
9370 var_set_int("$ENABLED_COLUMN_NAMES", 0);
9371 break;
9372 case Q_DISABLE_COLUMN_NAMES:
9373 disable_column_names= 1;
9374 var_set_int("$ENABLED_COLUMN_NAMES", 1);
9375 break;
9376 case Q_SOURCE: do_source(command); break;
9377 case Q_SLEEP: do_sleep(command, 0); break;
9378 case Q_REAL_SLEEP: do_sleep(command, 1); break;
9379 case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(command); break;
9380 case Q_INC: do_modify_var(command, DO_INC); break;
9381 case Q_DEC: do_modify_var(command, DO_DEC); break;
9382 case Q_ECHO: do_echo(command); command_executed++; break;
9383 case Q_SYSTEM: do_system(command); break;
9384 case Q_REMOVE_FILE: do_remove_file(command); break;
9385 case Q_REMOVE_FILES_WILDCARD: do_remove_files_wildcard(command); break;
9386 case Q_MKDIR: do_mkdir(command); break;
9387 case Q_RMDIR: do_rmdir(command); break;
9388 case Q_LIST_FILES: do_list_files(command); break;
9389 case Q_LIST_FILES_WRITE_FILE:
9390 do_list_files_write_file_command(command, FALSE);
9391 break;
9392 case Q_LIST_FILES_APPEND_FILE:
9393 do_list_files_write_file_command(command, TRUE);
9394 break;
9395 case Q_FILE_EXIST: do_file_exist(command); break;
9396 case Q_WRITE_FILE: do_write_file(command); break;
9397 case Q_APPEND_FILE: do_append_file(command); break;
9398 case Q_DIFF_FILES: do_diff_files(command); break;
9399 case Q_SEND_QUIT: do_send_quit(command); break;
9400 case Q_CHANGE_USER: do_change_user(command); break;
9401 case Q_CAT_FILE: do_cat_file(command); break;
9402 case Q_COPY_FILE: do_copy_file(command); break;
9403 case Q_MOVE_FILE: do_move_file(command); break;
9404 case Q_CHMOD_FILE: do_chmod_file(command); break;
9405 case Q_PERL: do_perl(command); break;
9406 case Q_RESULT_FORMAT_VERSION: do_result_format_version(command); break;
9407 case Q_DELIMITER:
9408 do_delimiter(command);
9409 break;
9410 case Q_DISPLAY_VERTICAL_RESULTS:
9411 display_result_vertically= TRUE;
9412 break;
9413 case Q_DISPLAY_HORIZONTAL_RESULTS:
9414 display_result_vertically= FALSE;
9415 break;
9416 case Q_SORTED_RESULT:
9417 /*
9418 Turn on sorting of result set, will be reset after next
9419 command
9420 */
9421 display_result_sorted= TRUE;
9422 break;
9423 case Q_LOWERCASE:
9424 /*
9425 Turn on lowercasing of result, will be reset after next
9426 command
9427 */
9428 display_result_lower= TRUE;
9429 break;
9430 case Q_LET: do_let(command); break;
9431 case Q_EVAL_RESULT:
9432 die("'eval_result' command is deprecated");
9433 case Q_EVAL:
9434 case Q_EVALP:
9435 case Q_QUERY_VERTICAL:
9436 case Q_QUERY_HORIZONTAL:
9437 if (command->query == command->query_buf)
9438 {
9439 /* Skip the first part of command, i.e query_xxx */
9440 command->query= command->first_argument;
9441 command->first_word_len= 0;
9442 }
9443 /* fall through */
9444 case Q_QUERY:
9445 case Q_REAP:
9446 {
9447 my_bool old_display_result_vertically= display_result_vertically;
9448 /* Default is full query, both reap and send */
9449 int flags= QUERY_REAP_FLAG | QUERY_SEND_FLAG;
9450
9451 if (q_send_flag)
9452 {
9453 /* Last command was an empty 'send' */
9454 flags= QUERY_SEND_FLAG;
9455 q_send_flag= 0;
9456 }
9457 else if (command->type == Q_REAP)
9458 {
9459 flags= QUERY_REAP_FLAG;
9460 }
9461
9462 if (command->type == Q_EVALP)
9463 flags |= QUERY_PRINT_ORIGINAL_FLAG;
9464
9465 /* Check for special property for this query */
9466 display_result_vertically|= (command->type == Q_QUERY_VERTICAL);
9467
9468 if (save_file[0])
9469 {
9470 if (!(command->require_file= strdup_root(&require_file_root,
9471 save_file)))
9472 die("out of memory for require_file");
9473 save_file[0]= 0;
9474 }
9475 run_query(cur_con, command, flags);
9476 command_executed++;
9477 command->last_argument= command->end;
9478
9479 /* Restore settings */
9480 display_result_vertically= old_display_result_vertically;
9481
9482 break;
9483 }
9484 case Q_SEND:
9485 case Q_SEND_EVAL:
9486 if (!*command->first_argument)
9487 {
9488 /*
9489 This is a send without arguments, it indicates that _next_ query
9490 should be send only
9491 */
9492 q_send_flag= 1;
9493 break;
9494 }
9495
9496 /* Remove "send" if this is first iteration */
9497 if (command->query == command->query_buf)
9498 command->query= command->first_argument;
9499
9500 /*
9501 run_query() can execute a query partially, depending on the flags.
9502 QUERY_SEND_FLAG flag without QUERY_REAP_FLAG tells it to just send
9503 the query and read the result some time later when reap instruction
9504 is given on this connection.
9505 */
9506 run_query(cur_con, command, QUERY_SEND_FLAG);
9507 command_executed++;
9508 command->last_argument= command->end;
9509 break;
9510 case Q_REQUIRE:
9511 do_get_file_name(command, save_file, sizeof(save_file));
9512 break;
9513 case Q_ERROR:
9514 do_get_errcodes(command);
9515 break;
9516 case Q_REPLACE:
9517 do_get_replace(command);
9518 break;
9519 case Q_REPLACE_REGEX:
9520 do_get_replace_regex(command);
9521 break;
9522 case Q_REPLACE_COLUMN:
9523 do_get_replace_column(command);
9524 break;
9525 case Q_SAVE_MASTER_POS: do_save_master_pos(); break;
9526 case Q_SYNC_WITH_MASTER: do_sync_with_master(command); break;
9527 case Q_SYNC_SLAVE_WITH_MASTER:
9528 {
9529 do_save_master_pos();
9530 if (*command->first_argument)
9531 select_connection(command);
9532 else
9533 select_connection_name("slave");
9534 do_sync_with_master2(command, 0, "");
9535 break;
9536 }
9537 case Q_COMMENT:
9538 {
9539 command->last_argument= command->end;
9540
9541 /* Don't output comments in v1 */
9542 if (opt_result_format_version == 1)
9543 break;
9544
9545 /* Don't output comments if query logging is off */
9546 if (disable_query_log)
9547 break;
9548
9549 /* Write comment's with two starting #'s to result file */
9550 const char* p= command->query;
9551 if (p && *p == '#' && *(p+1) == '#')
9552 {
9553 dynstr_append_mem(&ds_res, command->query, command->query_len);
9554 dynstr_append(&ds_res, "\n");
9555 }
9556 break;
9557 }
9558 case Q_EMPTY_LINE:
9559 /* Don't output newline in v1 */
9560 if (opt_result_format_version == 1)
9561 break;
9562
9563 /* Don't output newline if query logging is off */
9564 if (disable_query_log)
9565 break;
9566
9567 dynstr_append(&ds_res, "\n");
9568 break;
9569 case Q_PING:
9570 handle_command_error(command, mysql_ping(cur_con->mysql), -1);
9571 break;
9572 case Q_SEND_SHUTDOWN:
9573 handle_command_error(command,
9574 mysql_shutdown(cur_con->mysql,
9575 SHUTDOWN_DEFAULT), -1);
9576 break;
9577 case Q_SHUTDOWN_SERVER:
9578 do_shutdown_server(command);
9579 break;
9580 case Q_EXEC:
9581 do_exec(command);
9582 command_executed++;
9583 break;
9584 case Q_START_TIMER:
9585 /* Overwrite possible earlier start of timer */
9586 timer_start= timer_now();
9587 break;
9588 case Q_END_TIMER:
9589 /* End timer before ending mysqltest */
9590 timer_output();
9591 break;
9592 case Q_CHARACTER_SET:
9593 do_set_charset(command);
9594 break;
9595 case Q_DISABLE_PS_PROTOCOL:
9596 set_property(command, P_PS, 0);
9597 /* Close any open statements */
9598 close_statements();
9599 break;
9600 case Q_ENABLE_PS_PROTOCOL:
9601 set_property(command, P_PS, ps_protocol);
9602 break;
9603 case Q_DISABLE_NON_BLOCKING_API:
9604 non_blocking_api_enabled= 0;
9605 break;
9606 case Q_ENABLE_NON_BLOCKING_API:
9607 non_blocking_api_enabled= 1;
9608 break;
9609 case Q_DISABLE_RECONNECT:
9610 mysql_options(cur_con->mysql, MYSQL_OPT_RECONNECT, &my_false);
9611 break;
9612 case Q_ENABLE_RECONNECT:
9613 mysql_options(cur_con->mysql, MYSQL_OPT_RECONNECT, &my_true);
9614 /* Close any open statements - no reconnect, need new prepare */
9615 close_statements();
9616 break;
9617 case Q_DISABLE_PARSING:
9618 if (parsing_disabled == 0)
9619 parsing_disabled= 1;
9620 else
9621 report_or_die("Parsing is already disabled");
9622 break;
9623 case Q_ENABLE_PARSING:
9624 /*
9625 Ensure we don't get parsing_disabled < 0 as this would accidentally
9626 disable code we don't want to have disabled
9627 */
9628 if (parsing_disabled == 1)
9629 parsing_disabled= 0;
9630 else
9631 report_or_die("Parsing is already enabled");
9632 break;
9633 case Q_DIE:
9634 /* Abort test with error code and error message */
9635 die("%s", command->first_argument);
9636 break;
9637 case Q_EXIT:
9638 /* Stop processing any more commands */
9639 abort_flag= 1;
9640 break;
9641 case Q_SKIP:
9642 /* Eval the query, thus replacing all environment variables */
9643 dynstr_set(&ds_res, 0);
9644 do_eval(&ds_res, command->first_argument, command->end, FALSE);
9645 abort_not_supported_test("%s",ds_res.str);
9646 break;
9647 case Q_RESULT:
9648 die("result, deprecated command");
9649 break;
9650 default:
9651 processed= 0;
9652 break;
9653 }
9654 }
9655
9656 if (!processed)
9657 {
9658 current_line_inc= 0;
9659 switch (command->type) {
9660 case Q_WHILE: do_block(cmd_while, command); break;
9661 case Q_IF: do_block(cmd_if, command); break;
9662 case Q_END_BLOCK: do_done(command); break;
9663 default: current_line_inc = 1; break;
9664 }
9665 }
9666 else
9667 check_eol_junk(command->last_argument);
9668
9669 if (command->type != Q_ERROR &&
9670 command->type != Q_COMMENT)
9671 {
9672 /*
9673 As soon as any non "error" command or comment has been executed,
9674 the array with expected errors should be cleared
9675 */
9676 memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
9677 }
9678
9679 if (command_executed != last_command_executed || command->used_replace)
9680 {
9681 /*
9682 As soon as any command has been executed,
9683 the replace structures should be cleared
9684 */
9685 free_all_replace();
9686
9687 /* Also reset "sorted_result" and "lowercase"*/
9688 display_result_sorted= FALSE;
9689 display_result_lower= FALSE;
9690 }
9691 last_command_executed= command_executed;
9692
9693 parser.current_line += current_line_inc;
9694 if ( opt_mark_progress )
9695 mark_progress(command, parser.current_line);
9696
9697 /* Write result from command to log file immediately */
9698 log_file.write(&ds_res);
9699 log_file.flush();
9700 dynstr_set(&ds_res, 0);
9701 }
9702
9703 log_file.close();
9704
9705 start_lineno= 0;
9706 verbose_msg("... Done processing test commands.");
9707
9708 if (parsing_disabled)
9709 die("Test ended with parsing disabled");
9710
9711 /*
9712 The whole test has been executed _successfully_.
9713 Time to compare result or save it to record file.
9714 The entire output from test is in the log file
9715 */
9716 if (log_file.bytes_written())
9717 {
9718 if (result_file_name)
9719 {
9720 /* A result file has been specified */
9721
9722 if (record)
9723 {
9724 /* Recording */
9725
9726 /* save a copy of the log to result file */
9727 if (my_copy(log_file.file_name(), result_file_name, MYF(0)) != 0)
9728 die("Failed to copy '%s' to '%s', errno: %d",
9729 log_file.file_name(), result_file_name, errno);
9730
9731 }
9732 else
9733 {
9734 /* Check that the output from test is equal to result file */
9735 check_result();
9736 }
9737 }
9738 }
9739 else
9740 {
9741 /* Empty output is an error *unless* we also have an empty result file */
9742 if (! result_file_name || record ||
9743 compare_files (log_file.file_name(), result_file_name))
9744 {
9745 die("The test didn't produce any output");
9746 }
9747 else
9748 {
9749 empty_result= TRUE; /* Meaning empty was expected */
9750 }
9751 }
9752
9753 if (!command_executed && result_file_name && !empty_result)
9754 die("No queries executed but non-empty result file found!");
9755
9756 verbose_msg("Test has succeeded!");
9757 timer_output();
9758 /* Yes, if we got this far the test has succeeded! Sakila smiles */
9759 cleanup_and_exit(0);
9760 return 0; /* Keep compiler happy too */
9761}
9762
9763
9764/*
9765 A primitive timer that give results in milliseconds if the
9766 --timer-file=<filename> is given. The timer result is written
9767 to that file when the result is available. To not confuse
9768 mysql-test-run with an old obsolete result, we remove the file
9769 before executing any commands. The time we measure is
9770
9771 - If no explicit 'start_timer' or 'end_timer' is given in the
9772 test case, the timer measure how long we execute in mysqltest.
9773
9774 - If only 'start_timer' is given we measure how long we execute
9775 from that point until we terminate mysqltest.
9776
9777 - If only 'end_timer' is given we measure how long we execute
9778 from that we enter mysqltest to the 'end_timer' is command is
9779 executed.
9780
9781 - If both 'start_timer' and 'end_timer' are given we measure
9782 the time between executing the two commands.
9783*/
9784
9785void timer_output(void)
9786{
9787 if (timer_file)
9788 {
9789 char buf[32], *end;
9790 ulonglong timer= timer_now() - timer_start;
9791 end= longlong10_to_str(timer, buf, 10);
9792 str_to_file(timer_file,buf, (int) (end-buf));
9793 /* Timer has been written to the file, don't use it anymore */
9794 timer_file= 0;
9795 }
9796}
9797
9798
9799ulonglong timer_now(void)
9800{
9801 return my_interval_timer() / 1000000;
9802}
9803
9804
9805/*
9806 Get arguments for replace_columns. The syntax is:
9807 replace-column column_number to_string [column_number to_string ...]
9808 Where each argument may be quoted with ' or "
9809 A argument may also be a variable, in which case the value of the
9810 variable is replaced.
9811*/
9812
9813void do_get_replace_column(struct st_command *command)
9814{
9815 char *from= command->first_argument;
9816 char *buff, *start;
9817 DBUG_ENTER("get_replace_columns");
9818
9819 free_replace_column();
9820 if (!*from)
9821 die("Missing argument in %s", command->query);
9822
9823 /* Allocate a buffer for results */
9824 start= buff= (char*)my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
9825 while (*from)
9826 {
9827 char *to;
9828 uint column_number;
9829 to= get_string(&buff, &from, command);
9830 if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS)
9831 die("Wrong column number to replace_column in '%s'", command->query);
9832 if (!*from)
9833 die("Wrong number of arguments to replace_column in '%s'",
9834 command->query);
9835 to= get_string(&buff, &from, command);
9836 my_free(replace_column[column_number-1]);
9837 replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE));
9838 set_if_bigger(max_replace_column, column_number);
9839 }
9840 my_free(start);
9841 command->last_argument= command->end;
9842
9843 DBUG_VOID_RETURN;
9844}
9845
9846
9847void free_replace_column()
9848{
9849 uint i;
9850 for (i=0 ; i < max_replace_column ; i++)
9851 {
9852 if (replace_column[i])
9853 {
9854 my_free(replace_column[i]);
9855 replace_column[i]= 0;
9856 }
9857 }
9858 max_replace_column= 0;
9859}
9860
9861
9862/****************************************************************************/
9863/*
9864 Replace functions
9865*/
9866
9867/* Definitions for replace result */
9868
9869typedef struct st_pointer_array { /* when using array-strings */
9870 TYPELIB typelib; /* Pointer to strings */
9871 uchar *str; /* Strings is here */
9872 uint8 *flag; /* Flag about each var. */
9873 uint array_allocs,max_count,length,max_length;
9874} POINTER_ARRAY;
9875
9876struct st_replace *init_replace(char * *from, char * *to, uint count,
9877 char * word_end_chars);
9878int insert_pointer_name(POINTER_ARRAY *pa,char * name);
9879void free_pointer_array(POINTER_ARRAY *pa);
9880
9881/*
9882 Get arguments for replace. The syntax is:
9883 replace from to [from to ...]
9884 Where each argument may be quoted with ' or "
9885 A argument may also be a variable, in which case the value of the
9886 variable is replaced.
9887*/
9888
9889void do_get_replace(struct st_command *command)
9890{
9891 uint i;
9892 char *from= command->first_argument;
9893 char *buff, *start;
9894 char word_end_chars[256], *pos;
9895 POINTER_ARRAY to_array, from_array;
9896 DBUG_ENTER("get_replace");
9897
9898 free_replace();
9899
9900 bzero(&to_array,sizeof(to_array));
9901 bzero(&from_array,sizeof(from_array));
9902 if (!*from)
9903 die("Missing argument in %s", command->query);
9904 start= buff= (char*)my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
9905 while (*from)
9906 {
9907 char *to= buff;
9908 to= get_string(&buff, &from, command);
9909 if (!*from)
9910 die("Wrong number of arguments to replace_result in '%s'",
9911 command->query);
9912 fix_win_paths(to, from - to);
9913 insert_pointer_name(&from_array,to);
9914 to= get_string(&buff, &from, command);
9915 insert_pointer_name(&to_array,to);
9916 }
9917 for (i= 1,pos= word_end_chars ; i < 256 ; i++)
9918 if (my_isspace(charset_info,i))
9919 *pos++= i;
9920 *pos=0; /* End pointer */
9921 if (!(glob_replace= init_replace((char**) from_array.typelib.type_names,
9922 (char**) to_array.typelib.type_names,
9923 (uint) from_array.typelib.count,
9924 word_end_chars)))
9925 die("Can't initialize replace from '%s'", command->query);
9926 free_pointer_array(&from_array);
9927 free_pointer_array(&to_array);
9928 my_free(start);
9929 command->last_argument= command->end;
9930 DBUG_VOID_RETURN;
9931}
9932
9933
9934void free_replace()
9935{
9936 DBUG_ENTER("free_replace");
9937 my_free(glob_replace);
9938 glob_replace= NULL;
9939 DBUG_VOID_RETURN;
9940}
9941
9942
9943typedef struct st_replace {
9944 int found;
9945 struct st_replace *next[256];
9946} REPLACE;
9947
9948typedef struct st_replace_found {
9949 int found;
9950 uint to_offset;
9951 int from_offset;
9952 char *replace_string;
9953} REPLACE_STRING;
9954
9955
9956void replace_strings_append(REPLACE *rep, DYNAMIC_STRING* ds,
9957 const char *str)
9958{
9959 REPLACE *rep_pos;
9960 REPLACE_STRING *rep_str;
9961 const char *start, *from;
9962 DBUG_ENTER("replace_strings_append");
9963
9964 start= from= str;
9965 rep_pos=rep+1;
9966 for (;;)
9967 {
9968 /* Loop through states */
9969 DBUG_PRINT("info", ("Looping through states"));
9970 while (!rep_pos->found)
9971 rep_pos= rep_pos->next[(uchar) *from++];
9972
9973 /* Does this state contain a string to be replaced */
9974 if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string)
9975 {
9976 /* No match found */
9977 dynstr_append_mem(ds, start, from - start - 1);
9978 DBUG_PRINT("exit", ("Found no more string to replace, appended: %s", start));
9979 DBUG_VOID_RETURN;
9980 }
9981
9982 /* Found a string that needs to be replaced */
9983 DBUG_PRINT("info", ("found: %d, to_offset: %u, from_offset: %d, string: %s",
9984 rep_str->found, rep_str->to_offset,
9985 rep_str->from_offset, rep_str->replace_string));
9986
9987 /* Append part of original string before replace string */
9988 dynstr_append_mem(ds, start, (from - rep_str->to_offset) - start);
9989
9990 /* Append replace string */
9991 dynstr_append_mem(ds, rep_str->replace_string,
9992 strlen(rep_str->replace_string));
9993
9994 if (!*(from-=rep_str->from_offset) && rep_pos->found != 2)
9995 {
9996 /* End of from string */
9997 DBUG_PRINT("exit", ("Found end of from string"));
9998 DBUG_VOID_RETURN;
9999 }
10000 start= from;
10001 rep_pos=rep;
10002 }
10003}
10004
10005
10006/*
10007 Regex replace functions
10008*/
10009
10010
10011/* Stores regex substitutions */
10012
10013struct st_regex
10014{
10015 char* pattern; /* Pattern to be replaced */
10016 char* replace; /* String or expression to replace the pattern with */
10017 int icase; /* true if the match is case insensitive */
10018};
10019
10020int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace,
10021 char *string, int icase);
10022
10023bool parse_re_part(char *start_re, char *end_re,
10024 char **p, char *end, char **buf)
10025{
10026 if (*start_re != *end_re)
10027 {
10028 switch ((*start_re= *(*p)++)) {
10029 case '(': *end_re= ')'; break;
10030 case '[': *end_re= ']'; break;
10031 case '{': *end_re= '}'; break;
10032 case '<': *end_re= '>'; break;
10033 default: *end_re= *start_re;
10034 }
10035 }
10036
10037 while (*p < end && **p != *end_re)
10038 {
10039 if ((*p)[0] == '\\' && *p + 1 < end && (*p)[1] == *end_re)
10040 (*p)++;
10041
10042 *(*buf)++= *(*p)++;
10043 }
10044 *(*buf)++= 0;
10045
10046 (*p)++;
10047
10048 return *p > end;
10049}
10050
10051/*
10052 Initializes the regular substitution expression to be used in the
10053 result output of test.
10054
10055 Returns: st_replace_regex struct with pairs of substitutions
10056*/
10057void append_replace_regex(char*, char*, struct st_replace_regex*, char**);
10058
10059struct st_replace_regex* init_replace_regex(char* expr)
10060{
10061 char *expr_end, *buf_p;
10062 struct st_replace_regex* res;
10063 size_t expr_len= strlen(expr);
10064
10065 /* my_malloc() will die on fail with MY_FAE */
10066 res=(struct st_replace_regex*)my_malloc(
10067 sizeof(*res)+8192 ,MYF(MY_FAE+MY_WME));
10068 my_init_dynamic_array(&res->regex_arr,sizeof(struct st_regex), 128, 128, MYF(0));
10069
10070 expr_end= expr + expr_len;
10071 buf_p= (char*)res + sizeof(*res);
10072 append_replace_regex(expr, expr_end, res, &buf_p);
10073
10074 res->odd_buf_len= res->even_buf_len= 8192;
10075 res->even_buf= (char*)my_malloc(res->even_buf_len,MYF(MY_WME+MY_FAE));
10076 res->odd_buf= (char*)my_malloc(res->odd_buf_len,MYF(MY_WME+MY_FAE));
10077 res->buf= res->even_buf;
10078
10079 return res;
10080}
10081
10082
10083void append_replace_regex(char* expr, char *expr_end, struct st_replace_regex* res,
10084 char **buf_p)
10085{
10086 char* p, start_re, end_re= 1;
10087 struct st_regex reg;
10088
10089 p= expr;
10090
10091 /* for each regexp substitution statement */
10092 while (p < expr_end)
10093 {
10094 bzero(&reg,sizeof(reg));
10095 /* find the start of the statement */
10096 while (my_isspace(charset_info, *p) && p < expr_end)
10097 p++;
10098
10099 if (p >= expr_end)
10100 {
10101 if (res->regex_arr.elements)
10102 break;
10103 else
10104 goto err;
10105 }
10106
10107 start_re= 0;
10108 reg.pattern= *buf_p;
10109
10110 /* Allow variable for the *entire* list of replacements */
10111 if (*p == '$')
10112 {
10113 const char *v_end;
10114 VAR *val= var_get(p, &v_end, 0, 1);
10115
10116 if (val)
10117 {
10118 char *expr, *expr_end;
10119 expr= val->str_val;
10120 expr_end= expr + val->str_val_len;
10121 append_replace_regex(expr, expr_end, res, buf_p);
10122 }
10123
10124 p= (char *) v_end + 1;
10125 continue;
10126 }
10127 else
10128 {
10129 if (parse_re_part(&start_re, &end_re, &p, expr_end, buf_p))
10130 goto err;
10131
10132 reg.replace= *buf_p;
10133 if (parse_re_part(&start_re, &end_re, &p, expr_end, buf_p))
10134 goto err;
10135 }
10136
10137 /* Check if we should do matching case insensitive */
10138 if (p < expr_end && *p == 'i')
10139 {
10140 p++;
10141 reg.icase= 1;
10142 }
10143
10144 /* done parsing the statement, now place it in regex_arr */
10145 if (insert_dynamic(&res->regex_arr, &reg))
10146 die("Out of memory");
10147 }
10148
10149 return;
10150
10151err:
10152 my_free(res);
10153 die("Error parsing replace_regex \"%s\"", expr);
10154}
10155
10156/*
10157 Execute all substitutions on val.
10158
10159 Returns: true if substituition was made, false otherwise
10160 Side-effect: Sets r->buf to be the buffer with all substitutions done.
10161
10162 IN:
10163 struct st_replace_regex* r
10164 char* val
10165 Out:
10166 struct st_replace_regex* r
10167 r->buf points at the resulting buffer
10168 r->even_buf and r->odd_buf might have been reallocated
10169 r->even_buf_len and r->odd_buf_len might have been changed
10170
10171 TODO: at some point figure out if there is a way to do everything
10172 in one pass
10173*/
10174
10175int multi_reg_replace(struct st_replace_regex* r,char* val)
10176{
10177 uint i;
10178 char* in_buf, *out_buf;
10179 int* buf_len_p;
10180
10181 in_buf= val;
10182 out_buf= r->even_buf;
10183 buf_len_p= &r->even_buf_len;
10184 r->buf= 0;
10185
10186 /* For each substitution, do the replace */
10187 for (i= 0; i < r->regex_arr.elements; i++)
10188 {
10189 struct st_regex re;
10190 char* save_out_buf= out_buf;
10191
10192 get_dynamic(&r->regex_arr, &re, i);
10193
10194 if (!reg_replace(&out_buf, buf_len_p, re.pattern, re.replace,
10195 in_buf, re.icase))
10196 {
10197 /* if the buffer has been reallocated, make adjustements */
10198 if (save_out_buf != out_buf)
10199 {
10200 if (save_out_buf == r->even_buf)
10201 r->even_buf= out_buf;
10202 else
10203 r->odd_buf= out_buf;
10204 }
10205
10206 r->buf= out_buf;
10207 if (in_buf == val)
10208 in_buf= r->odd_buf;
10209
10210 swap_variables(char*,in_buf,out_buf);
10211
10212 buf_len_p= (out_buf == r->even_buf) ? &r->even_buf_len :
10213 &r->odd_buf_len;
10214 }
10215 }
10216
10217 return (r->buf == 0);
10218}
10219
10220/*
10221 Parse the regular expression to be used in all result files
10222 from now on.
10223
10224 The syntax is --replace_regex /from/to/i /from/to/i ...
10225 i means case-insensitive match. If omitted, the match is
10226 case-sensitive
10227
10228*/
10229void do_get_replace_regex(struct st_command *command)
10230{
10231 char *expr= command->first_argument;
10232 free_replace_regex();
10233 if (expr && *expr && !(glob_replace_regex=init_replace_regex(expr)))
10234 die("Could not init replace_regex");
10235 command->last_argument= command->end;
10236}
10237
10238void free_replace_regex()
10239{
10240 if (glob_replace_regex)
10241 {
10242 delete_dynamic(&glob_replace_regex->regex_arr);
10243 my_free(glob_replace_regex->even_buf);
10244 my_free(glob_replace_regex->odd_buf);
10245 my_free(glob_replace_regex);
10246 glob_replace_regex=0;
10247 }
10248}
10249
10250
10251
10252/*
10253 auxiluary macro used by reg_replace
10254 makes sure the result buffer has sufficient length
10255*/
10256#define SECURE_REG_BUF if (buf_len < need_buf_len) \
10257 { \
10258 ssize_t off= res_p - buf; \
10259 buf= (char*)my_realloc(buf,need_buf_len,MYF(MY_WME+MY_FAE)); \
10260 res_p= buf + off; \
10261 buf_len= need_buf_len; \
10262 } \
10263 \
10264/*
10265 Performs a regex substitution
10266
10267 IN:
10268
10269 buf_p - result buffer pointer. Will change if reallocated
10270 buf_len_p - result buffer length. Will change if the buffer is reallocated
10271 pattern - regexp pattern to match
10272 replace - replacement expression
10273 string - the string to perform substitutions in
10274 icase - flag, if set to 1 the match is case insensitive
10275*/
10276int reg_replace(char** buf_p, int* buf_len_p, char *pattern,
10277 char *replace, char *string, int icase)
10278{
10279 regex_t r;
10280 regmatch_t *subs;
10281 char *replace_end;
10282 char *buf= *buf_p;
10283 size_t len;
10284 size_t buf_len, need_buf_len;
10285 int cflags= REG_EXTENDED | REG_DOTALL;
10286 int err_code;
10287 char *res_p,*str_p,*str_end;
10288
10289 DBUG_ASSERT(*buf_len_p > 0);
10290
10291 buf_len= (size_t)*buf_len_p;
10292 len= strlen(string);
10293 str_end= string + len;
10294
10295 /* start with a buffer of a reasonable size that hopefully will not
10296 need to be reallocated
10297 */
10298 need_buf_len= len * 2 + 1;
10299 res_p= buf;
10300
10301 SECURE_REG_BUF
10302
10303 if (icase)
10304 cflags|= REG_ICASE;
10305
10306 if ((err_code= regcomp(&r,pattern,cflags)))
10307 {
10308 check_regerr(&r,err_code);
10309 return 1;
10310 }
10311
10312 subs= (regmatch_t*)my_malloc(sizeof(regmatch_t) * (r.re_nsub+1),
10313 MYF(MY_WME+MY_FAE));
10314
10315 *res_p= 0;
10316 str_p= string;
10317 replace_end= replace + strlen(replace);
10318
10319 /* for each pattern match instance perform a replacement */
10320 while (!err_code)
10321 {
10322 /* find the match */
10323 err_code= regexec(&r,str_p, r.re_nsub+1, subs,
10324 (str_p == string) ? 0 : REG_NOTBOL);
10325
10326 /* if regular expression error (eg. bad syntax, or out of memory) */
10327 if (err_code && err_code != REG_NOMATCH)
10328 {
10329 check_regerr(&r,err_code);
10330 regfree(&r);
10331 return 1;
10332 }
10333
10334 /* if match found */
10335 if (!err_code)
10336 {
10337 char* expr_p= replace;
10338 int c;
10339
10340 /*
10341 we need at least what we have so far in the buffer + the part
10342 before this match
10343 */
10344 need_buf_len= (res_p - buf) + (int) subs[0].rm_so;
10345
10346 /* on this pass, calculate the memory for the result buffer */
10347 while (expr_p < replace_end)
10348 {
10349 int back_ref_num= -1;
10350 c= *expr_p;
10351
10352 if (c == '\\' && expr_p + 1 < replace_end)
10353 {
10354 back_ref_num= (int) (expr_p[1] - '0');
10355 }
10356
10357 /* found a valid back_ref (eg. \1)*/
10358 if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub)
10359 {
10360 regoff_t start_off, end_off;
10361 if ((start_off=subs[back_ref_num].rm_so) > -1 &&
10362 (end_off=subs[back_ref_num].rm_eo) > -1)
10363 {
10364 need_buf_len += (int) (end_off - start_off);
10365 }
10366 expr_p += 2;
10367 }
10368 else
10369 {
10370 expr_p++;
10371 need_buf_len++;
10372 }
10373 }
10374 need_buf_len++;
10375 /*
10376 now that we know the size of the buffer,
10377 make sure it is big enough
10378 */
10379 SECURE_REG_BUF
10380
10381 /* copy the pre-match part */
10382 if (subs[0].rm_so)
10383 {
10384 memcpy(res_p, str_p, (size_t) subs[0].rm_so);
10385 res_p+= subs[0].rm_so;
10386 }
10387
10388 expr_p= replace;
10389
10390 /* copy the match and expand back_refs */
10391 while (expr_p < replace_end)
10392 {
10393 int back_ref_num= -1;
10394 c= *expr_p;
10395
10396 if (c == '\\' && expr_p + 1 < replace_end)
10397 {
10398 back_ref_num= expr_p[1] - '0';
10399 }
10400
10401 if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub)
10402 {
10403 regoff_t start_off, end_off;
10404 if ((start_off=subs[back_ref_num].rm_so) > -1 &&
10405 (end_off=subs[back_ref_num].rm_eo) > -1)
10406 {
10407 int block_len= (int) (end_off - start_off);
10408 memcpy(res_p,str_p + start_off, block_len);
10409 res_p += block_len;
10410 }
10411 expr_p += 2;
10412 }
10413 else
10414 {
10415 *res_p++ = *expr_p++;
10416 }
10417 }
10418
10419 /* handle the post-match part */
10420 if (subs[0].rm_so == subs[0].rm_eo)
10421 {
10422 if (str_p + subs[0].rm_so >= str_end)
10423 break;
10424 str_p += subs[0].rm_eo ;
10425 *res_p++ = *str_p++;
10426 }
10427 else
10428 {
10429 str_p += subs[0].rm_eo;
10430 }
10431 }
10432 else /* no match this time, just copy the string as is */
10433 {
10434 size_t left_in_str= str_end-str_p;
10435 need_buf_len= (res_p-buf) + left_in_str;
10436 SECURE_REG_BUF
10437 memcpy(res_p,str_p,left_in_str);
10438 res_p += left_in_str;
10439 str_p= str_end;
10440 }
10441 }
10442 my_free(subs);
10443 regfree(&r);
10444 *res_p= 0;
10445 *buf_p= buf;
10446 *buf_len_p= (int)buf_len;
10447 return 0;
10448}
10449
10450
10451#ifndef WORD_BIT
10452#define WORD_BIT (8*sizeof(uint))
10453#endif
10454
10455#define SET_MALLOC_HUNC 64
10456#define LAST_CHAR_CODE 259
10457
10458typedef struct st_rep_set {
10459 uint *bits; /* Pointer to used sets */
10460 short next[LAST_CHAR_CODE]; /* Pointer to next sets */
10461 uint found_len; /* Best match to date */
10462 int found_offset;
10463 uint table_offset;
10464 uint size_of_bits; /* For convinience */
10465} REP_SET;
10466
10467typedef struct st_rep_sets {
10468 uint count; /* Number of sets */
10469 uint extra; /* Extra sets in buffer */
10470 uint invisible; /* Sets not chown */
10471 uint size_of_bits;
10472 REP_SET *set,*set_buffer;
10473 uint *bit_buffer;
10474} REP_SETS;
10475
10476typedef struct st_found_set {
10477 uint table_offset;
10478 int found_offset;
10479} FOUND_SET;
10480
10481typedef struct st_follow {
10482 int chr;
10483 uint table_offset;
10484 uint len;
10485} FOLLOWS;
10486
10487
10488int init_sets(REP_SETS *sets,uint states);
10489REP_SET *make_new_set(REP_SETS *sets);
10490void make_sets_invisible(REP_SETS *sets);
10491void free_last_set(REP_SETS *sets);
10492void free_sets(REP_SETS *sets);
10493void internal_set_bit(REP_SET *set, uint bit);
10494void internal_clear_bit(REP_SET *set, uint bit);
10495void or_bits(REP_SET *to,REP_SET *from);
10496void copy_bits(REP_SET *to,REP_SET *from);
10497int cmp_bits(REP_SET *set1,REP_SET *set2);
10498int get_next_bit(REP_SET *set,uint lastpos);
10499int find_set(REP_SETS *sets,REP_SET *find);
10500int find_found(FOUND_SET *found_set,uint table_offset,
10501 int found_offset);
10502uint start_at_word(char * pos);
10503uint end_of_word(char * pos);
10504
10505static uint found_sets=0;
10506
10507
10508uint replace_len(char * str)
10509{
10510 uint len=0;
10511 while (*str)
10512 {
10513 str++;
10514 len++;
10515 }
10516 return len;
10517}
10518
10519/* Init a replace structure for further calls */
10520
10521REPLACE *init_replace(char * *from, char * *to,uint count,
10522 char * word_end_chars)
10523{
10524 static const int SPACE_CHAR= 256;
10525 static const int END_OF_LINE= 258;
10526
10527 uint i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr;
10528 int used_sets,chr,default_state;
10529 char used_chars[LAST_CHAR_CODE],is_word_end[256];
10530 char * pos, *to_pos, **to_array;
10531 REP_SETS sets;
10532 REP_SET *set,*start_states,*word_states,*new_set;
10533 FOLLOWS *follow,*follow_ptr;
10534 REPLACE *replace;
10535 FOUND_SET *found_set;
10536 REPLACE_STRING *rep_str;
10537 DBUG_ENTER("init_replace");
10538
10539 /* Count number of states */
10540 for (i=result_len=max_length=0 , states=2 ; i < count ; i++)
10541 {
10542 len=replace_len(from[i]);
10543 if (!len)
10544 {
10545 errno=EINVAL;
10546 DBUG_RETURN(0);
10547 }
10548 states+=len+1;
10549 result_len+=(uint) strlen(to[i])+1;
10550 if (len > max_length)
10551 max_length=len;
10552 }
10553 bzero(is_word_end, sizeof(is_word_end));
10554 for (i=0 ; word_end_chars[i] ; i++)
10555 is_word_end[(uchar) word_end_chars[i]]=1;
10556
10557 if (init_sets(&sets,states))
10558 DBUG_RETURN(0);
10559 found_sets=0;
10560 if (!(found_set= (FOUND_SET*) my_malloc(sizeof(FOUND_SET)*max_length*count,
10561 MYF(MY_WME))))
10562 {
10563 free_sets(&sets);
10564 DBUG_RETURN(0);
10565 }
10566 (void) make_new_set(&sets); /* Set starting set */
10567 make_sets_invisible(&sets); /* Hide previus sets */
10568 used_sets=-1;
10569 word_states=make_new_set(&sets); /* Start of new word */
10570 start_states=make_new_set(&sets); /* This is first state */
10571 if (!(follow=(FOLLOWS*) my_malloc((states+2)*sizeof(FOLLOWS),MYF(MY_WME))))
10572 {
10573 free_sets(&sets);
10574 my_free(found_set);
10575 DBUG_RETURN(0);
10576 }
10577
10578 /* Init follow_ptr[] */
10579 for (i=0, states=1, follow_ptr=follow+1 ; i < count ; i++)
10580 {
10581 if (from[i][0] == '\\' && from[i][1] == '^')
10582 {
10583 internal_set_bit(start_states,states+1);
10584 if (!from[i][2])
10585 {
10586 start_states->table_offset=i;
10587 start_states->found_offset=1;
10588 }
10589 }
10590 else if (from[i][0] == '\\' && from[i][1] == '$')
10591 {
10592 internal_set_bit(start_states,states);
10593 internal_set_bit(word_states,states);
10594 if (!from[i][2] && start_states->table_offset == (uint) ~0)
10595 {
10596 start_states->table_offset=i;
10597 start_states->found_offset=0;
10598 }
10599 }
10600 else
10601 {
10602 internal_set_bit(word_states,states);
10603 if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2]))
10604 internal_set_bit(start_states,states+1);
10605 else
10606 internal_set_bit(start_states,states);
10607 }
10608 for (pos=from[i], len=0; *pos ; pos++)
10609 {
10610 follow_ptr->chr= (uchar) *pos;
10611 follow_ptr->table_offset=i;
10612 follow_ptr->len= ++len;
10613 follow_ptr++;
10614 }
10615 follow_ptr->chr=0;
10616 follow_ptr->table_offset=i;
10617 follow_ptr->len=len;
10618 follow_ptr++;
10619 states+=(uint) len+1;
10620 }
10621
10622
10623 for (set_nr=0,pos=0 ; set_nr < sets.count ; set_nr++)
10624 {
10625 set=sets.set+set_nr;
10626 default_state= 0; /* Start from beginning */
10627
10628 /* If end of found-string not found or start-set with current set */
10629
10630 for (i= (uint) ~0; (i=get_next_bit(set,i)) ;)
10631 {
10632 if (!follow[i].chr)
10633 {
10634 if (! default_state)
10635 default_state= find_found(found_set,set->table_offset,
10636 set->found_offset+1);
10637 }
10638 }
10639 copy_bits(sets.set+used_sets,set); /* Save set for changes */
10640 if (!default_state)
10641 or_bits(sets.set+used_sets,sets.set); /* Can restart from start */
10642
10643 /* Find all chars that follows current sets */
10644 bzero(used_chars, sizeof(used_chars));
10645 for (i= (uint) ~0; (i=get_next_bit(sets.set+used_sets,i)) ;)
10646 {
10647 used_chars[follow[i].chr]=1;
10648 if ((follow[i].chr == SPACE_CHAR && !follow[i+1].chr &&
10649 follow[i].len > 1) || follow[i].chr == END_OF_LINE)
10650 used_chars[0]=1;
10651 }
10652
10653 /* Mark word_chars used if \b is in state */
10654 if (used_chars[SPACE_CHAR])
10655 for (pos= word_end_chars ; *pos ; pos++)
10656 used_chars[(int) (uchar) *pos] = 1;
10657
10658 /* Handle other used characters */
10659 for (chr= 0 ; chr < 256 ; chr++)
10660 {
10661 if (! used_chars[chr])
10662 set->next[chr]= chr ? default_state : -1;
10663 else
10664 {
10665 new_set=make_new_set(&sets);
10666 set=sets.set+set_nr; /* if realloc */
10667 new_set->table_offset=set->table_offset;
10668 new_set->found_len=set->found_len;
10669 new_set->found_offset=set->found_offset+1;
10670 found_end=0;
10671
10672 for (i= (uint) ~0 ; (i=get_next_bit(sets.set+used_sets,i)) ; )
10673 {
10674 if (!follow[i].chr || follow[i].chr == chr ||
10675 (follow[i].chr == SPACE_CHAR &&
10676 (is_word_end[chr] ||
10677 (!chr && follow[i].len > 1 && ! follow[i+1].chr))) ||
10678 (follow[i].chr == END_OF_LINE && ! chr))
10679 {
10680 if ((! chr || (follow[i].chr && !follow[i+1].chr)) &&
10681 follow[i].len > found_end)
10682 found_end=follow[i].len;
10683 if (chr && follow[i].chr)
10684 internal_set_bit(new_set,i+1); /* To next set */
10685 else
10686 internal_set_bit(new_set,i);
10687 }
10688 }
10689 if (found_end)
10690 {
10691 new_set->found_len=0; /* Set for testing if first */
10692 bits_set=0;
10693 for (i= (uint) ~0; (i=get_next_bit(new_set,i)) ;)
10694 {
10695 if ((follow[i].chr == SPACE_CHAR ||
10696 follow[i].chr == END_OF_LINE) && ! chr)
10697 bit_nr=i+1;
10698 else
10699 bit_nr=i;
10700 if (follow[bit_nr-1].len < found_end ||
10701 (new_set->found_len &&
10702 (chr == 0 || !follow[bit_nr].chr)))
10703 internal_clear_bit(new_set,i);
10704 else
10705 {
10706 if (chr == 0 || !follow[bit_nr].chr)
10707 { /* best match */
10708 new_set->table_offset=follow[bit_nr].table_offset;
10709 if (chr || (follow[i].chr == SPACE_CHAR ||
10710 follow[i].chr == END_OF_LINE))
10711 new_set->found_offset=found_end; /* New match */
10712 new_set->found_len=found_end;
10713 }
10714 bits_set++;
10715 }
10716 }
10717 if (bits_set == 1)
10718 {
10719 set->next[chr] = find_found(found_set,
10720 new_set->table_offset,
10721 new_set->found_offset);
10722 free_last_set(&sets);
10723 }
10724 else
10725 set->next[chr] = find_set(&sets,new_set);
10726 }
10727 else
10728 set->next[chr] = find_set(&sets,new_set);
10729 }
10730 }
10731 }
10732
10733 /* Alloc replace structure for the replace-state-machine */
10734
10735 if ((replace=(REPLACE*) my_malloc(sizeof(REPLACE)*(sets.count)+
10736 sizeof(REPLACE_STRING)*(found_sets+1)+
10737 sizeof(char *)*count+result_len,
10738 MYF(MY_WME | MY_ZEROFILL))))
10739 {
10740 rep_str=(REPLACE_STRING*) (replace+sets.count);
10741 to_array= (char **) (rep_str+found_sets+1);
10742 to_pos=(char *) (to_array+count);
10743 for (i=0 ; i < count ; i++)
10744 {
10745 to_array[i]=to_pos;
10746 to_pos=strmov(to_pos,to[i])+1;
10747 }
10748 rep_str[0].found=1;
10749 rep_str[0].replace_string=0;
10750 for (i=1 ; i <= found_sets ; i++)
10751 {
10752 pos=from[found_set[i-1].table_offset];
10753 rep_str[i].found= !memcmp(pos, "\\^", 3) ? 2 : 1;
10754 rep_str[i].replace_string=to_array[found_set[i-1].table_offset];
10755 rep_str[i].to_offset=found_set[i-1].found_offset-start_at_word(pos);
10756 rep_str[i].from_offset=found_set[i-1].found_offset-replace_len(pos)+
10757 end_of_word(pos);
10758 }
10759 for (i=0 ; i < sets.count ; i++)
10760 {
10761 for (j=0 ; j < 256 ; j++)
10762 if (sets.set[i].next[j] >= 0)
10763 replace[i].next[j]=replace+sets.set[i].next[j];
10764 else
10765 replace[i].next[j]=(REPLACE*) (rep_str+(-sets.set[i].next[j]-1));
10766 }
10767 }
10768 my_free(follow);
10769 free_sets(&sets);
10770 my_free(found_set);
10771 DBUG_PRINT("exit",("Replace table has %d states",sets.count));
10772 DBUG_RETURN(replace);
10773}
10774
10775
10776int init_sets(REP_SETS *sets,uint states)
10777{
10778 bzero(sets, sizeof(*sets));
10779 sets->size_of_bits=((states+7)/8);
10780 if (!(sets->set_buffer=(REP_SET*) my_malloc(sizeof(REP_SET)*SET_MALLOC_HUNC,
10781 MYF(MY_WME))))
10782 return 1;
10783 if (!(sets->bit_buffer=(uint*) my_malloc(sizeof(uint)*sets->size_of_bits*
10784 SET_MALLOC_HUNC,MYF(MY_WME))))
10785 {
10786 my_free(sets->set);
10787 return 1;
10788 }
10789 return 0;
10790}
10791
10792/* Make help sets invisible for nicer codeing */
10793
10794void make_sets_invisible(REP_SETS *sets)
10795{
10796 sets->invisible=sets->count;
10797 sets->set+=sets->count;
10798 sets->count=0;
10799}
10800
10801REP_SET *make_new_set(REP_SETS *sets)
10802{
10803 uint i,count,*bit_buffer;
10804 REP_SET *set;
10805 if (sets->extra)
10806 {
10807 sets->extra--;
10808 set=sets->set+ sets->count++;
10809 bzero(set->bits, sizeof(uint) * sets->size_of_bits);
10810 bzero(&set->next[0], sizeof(set->next[0]) * LAST_CHAR_CODE);
10811 set->found_offset=0;
10812 set->found_len=0;
10813 set->table_offset= (uint) ~0;
10814 set->size_of_bits=sets->size_of_bits;
10815 return set;
10816 }
10817 count=sets->count+sets->invisible+SET_MALLOC_HUNC;
10818 if (!(set=(REP_SET*) my_realloc(sets->set_buffer, sizeof(REP_SET)*count,
10819 MYF(MY_WME))))
10820 return 0;
10821 sets->set_buffer=set;
10822 sets->set=set+sets->invisible;
10823 if (!(bit_buffer=(uint*) my_realloc(sets->bit_buffer,
10824 (sizeof(uint)*sets->size_of_bits)*count,
10825 MYF(MY_WME))))
10826 return 0;
10827 sets->bit_buffer=bit_buffer;
10828 for (i=0 ; i < count ; i++)
10829 {
10830 sets->set_buffer[i].bits=bit_buffer;
10831 bit_buffer+=sets->size_of_bits;
10832 }
10833 sets->extra=SET_MALLOC_HUNC;
10834 return make_new_set(sets);
10835}
10836
10837void free_last_set(REP_SETS *sets)
10838{
10839 sets->count--;
10840 sets->extra++;
10841 return;
10842}
10843
10844void free_sets(REP_SETS *sets)
10845{
10846 my_free(sets->set_buffer);
10847 my_free(sets->bit_buffer);
10848 return;
10849}
10850
10851void internal_set_bit(REP_SET *set, uint bit)
10852{
10853 set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
10854 return;
10855}
10856
10857void internal_clear_bit(REP_SET *set, uint bit)
10858{
10859 set->bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT));
10860 return;
10861}
10862
10863
10864void or_bits(REP_SET *to,REP_SET *from)
10865{
10866 uint i;
10867 for (i=0 ; i < to->size_of_bits ; i++)
10868 to->bits[i]|=from->bits[i];
10869 return;
10870}
10871
10872void copy_bits(REP_SET *to,REP_SET *from)
10873{
10874 memcpy(to->bits, from->bits,
10875 (size_t) (sizeof(uint) * to->size_of_bits));
10876}
10877
10878int cmp_bits(REP_SET *set1,REP_SET *set2)
10879{
10880 return memcmp(set1->bits, set2->bits,
10881 sizeof(uint) * set1->size_of_bits);
10882}
10883
10884
10885/* Get next set bit from set. */
10886
10887int get_next_bit(REP_SET *set,uint lastpos)
10888{
10889 uint pos,*start,*end,bits;
10890
10891 start=set->bits+ ((lastpos+1) / WORD_BIT);
10892 end=set->bits + set->size_of_bits;
10893 bits=start[0] & ~((1 << ((lastpos+1) % WORD_BIT)) -1);
10894
10895 while (! bits && ++start < end)
10896 bits=start[0];
10897 if (!bits)
10898 return 0;
10899 pos=(uint) (start-set->bits)*WORD_BIT;
10900 while (! (bits & 1))
10901 {
10902 bits>>=1;
10903 pos++;
10904 }
10905 return pos;
10906}
10907
10908/* find if there is a same set in sets. If there is, use it and
10909 free given set, else put in given set in sets and return its
10910 position */
10911
10912int find_set(REP_SETS *sets,REP_SET *find)
10913{
10914 uint i;
10915 for (i=0 ; i < sets->count-1 ; i++)
10916 {
10917 if (!cmp_bits(sets->set+i,find))
10918 {
10919 free_last_set(sets);
10920 return i;
10921 }
10922 }
10923 return i; /* return new position */
10924}
10925
10926/* find if there is a found_set with same table_offset & found_offset
10927 If there is return offset to it, else add new offset and return pos.
10928 Pos returned is -offset-2 in found_set_structure because it is
10929 saved in set->next and set->next[] >= 0 points to next set and
10930 set->next[] == -1 is reserved for end without replaces.
10931*/
10932
10933int find_found(FOUND_SET *found_set,uint table_offset, int found_offset)
10934{
10935 int i;
10936 for (i=0 ; (uint) i < found_sets ; i++)
10937 if (found_set[i].table_offset == table_offset &&
10938 found_set[i].found_offset == found_offset)
10939 return -i-2;
10940 found_set[i].table_offset=table_offset;
10941 found_set[i].found_offset=found_offset;
10942 found_sets++;
10943 return -i-2; /* return new position */
10944}
10945
10946/* Return 1 if regexp starts with \b or ends with \b*/
10947
10948uint start_at_word(char * pos)
10949{
10950 return (((!memcmp(pos, "\\b",2) && pos[2]) ||
10951 !memcmp(pos, "\\^", 2)) ? 1 : 0);
10952}
10953
10954uint end_of_word(char * pos)
10955{
10956 char * end=strend(pos);
10957 return ((end > pos+2 && !memcmp(end-2, "\\b", 2)) ||
10958 (end >= pos+2 && !memcmp(end-2, "\\$",2))) ? 1 : 0;
10959}
10960
10961/****************************************************************************
10962 * Handle replacement of strings
10963 ****************************************************************************/
10964
10965#define PC_MALLOC 256 /* Bytes for pointers */
10966#define PS_MALLOC 512 /* Bytes for data */
10967
10968int insert_pointer_name(POINTER_ARRAY *pa,char * name)
10969{
10970 uint i,length,old_count;
10971 uchar *new_pos;
10972 const char **new_array;
10973 DBUG_ENTER("insert_pointer_name");
10974
10975 if (! pa->typelib.count)
10976 {
10977 if (!(pa->typelib.type_names=(const char **)
10978 my_malloc(((PC_MALLOC-MALLOC_OVERHEAD)/
10979 (sizeof(char *)+sizeof(*pa->flag))*
10980 (sizeof(char *)+sizeof(*pa->flag))),MYF(MY_WME))))
10981 DBUG_RETURN(-1);
10982 if (!(pa->str= (uchar*) my_malloc(PS_MALLOC - MALLOC_OVERHEAD,
10983 MYF(MY_WME))))
10984 {
10985 my_free(pa->typelib.type_names);
10986 DBUG_RETURN (-1);
10987 }
10988 pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(uchar*)+
10989 sizeof(*pa->flag));
10990 pa->flag= (uint8*) (pa->typelib.type_names+pa->max_count);
10991 pa->length=0;
10992 pa->max_length=PS_MALLOC-MALLOC_OVERHEAD;
10993 pa->array_allocs=1;
10994 }
10995 length=(uint) strlen(name)+1;
10996 if (pa->length+length >= pa->max_length)
10997 {
10998 if (!(new_pos= (uchar*) my_realloc(pa->str, pa->length + length + PS_MALLOC,
10999 MYF(MY_WME))))
11000 DBUG_RETURN(1);
11001 if (new_pos != pa->str)
11002 {
11003 my_ptrdiff_t diff=PTR_BYTE_DIFF(new_pos,pa->str);
11004 for (i=0 ; i < pa->typelib.count ; i++)
11005 pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff,
11006 char*);
11007 pa->str=new_pos;
11008 }
11009 pa->max_length= pa->length+length+PS_MALLOC;
11010 }
11011 if (pa->typelib.count >= pa->max_count-1)
11012 {
11013 int len;
11014 pa->array_allocs++;
11015 len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD);
11016 if (!(new_array=(const char **) my_realloc(pa->typelib.type_names,
11017 len/
11018 (sizeof(uchar*)+sizeof(*pa->flag))*
11019 (sizeof(uchar*)+sizeof(*pa->flag)),
11020 MYF(MY_WME))))
11021 DBUG_RETURN(1);
11022 pa->typelib.type_names=new_array;
11023 old_count=pa->max_count;
11024 pa->max_count=len/(sizeof(uchar*) + sizeof(*pa->flag));
11025 pa->flag= (uint8*) (pa->typelib.type_names+pa->max_count);
11026 memcpy(pa->flag, (pa->typelib.type_names +old_count),
11027 old_count*sizeof(*pa->flag));
11028 }
11029 pa->flag[pa->typelib.count]=0; /* Reset flag */
11030 pa->typelib.type_names[pa->typelib.count++]= (char*) pa->str+pa->length;
11031 pa->typelib.type_names[pa->typelib.count]= NullS; /* Put end-mark */
11032 (void) strmov((char*) pa->str + pa->length,name);
11033 pa->length+=length;
11034 DBUG_RETURN(0);
11035} /* insert_pointer_name */
11036
11037
11038/* free pointer array */
11039
11040void free_pointer_array(POINTER_ARRAY *pa)
11041{
11042 if (pa->typelib.count)
11043 {
11044 pa->typelib.count=0;
11045 my_free(pa->typelib.type_names);
11046 pa->typelib.type_names=0;
11047 my_free(pa->str);
11048 }
11049} /* free_pointer_array */
11050
11051
11052/* Functions that uses replace and replace_regex */
11053
11054/* Append the string to ds, with optional replace */
11055void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, size_t len)
11056{
11057 char lower[1024];
11058
11059 if (len < sizeof(lower) - 1)
11060 {
11061 if (display_result_lower)
11062 {
11063 /* Convert to lower case, and do this first */
11064 char *c= lower;
11065 for (const char *v= val; *v; v++)
11066 *c++= my_tolower(charset_info, *v);
11067 *c= '\0';
11068 /* Copy from this buffer instead */
11069 }
11070 else
11071 memcpy(lower, val, len+1);
11072 fix_win_paths(lower, len);
11073 val= lower;
11074 }
11075
11076 if (glob_replace_regex)
11077 {
11078 /* Regex replace */
11079 if (!multi_reg_replace(glob_replace_regex, (char*)val))
11080 {
11081 val= glob_replace_regex->buf;
11082 len= strlen(val);
11083 }
11084 }
11085
11086 if (glob_replace)
11087 {
11088 /* Normal replace */
11089 replace_strings_append(glob_replace, ds, val);
11090 }
11091 else
11092 dynstr_append_mem(ds, val, len);
11093}
11094
11095
11096/* Append zero-terminated string to ds, with optional replace */
11097void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val)
11098{
11099 replace_dynstr_append_mem(ds, val, strlen(val));
11100}
11101
11102/* Append uint to ds, with optional replace */
11103void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val)
11104{
11105 char buff[22]; /* This should be enough for any int */
11106 char *end= longlong10_to_str(val, buff, 10);
11107 replace_dynstr_append_mem(ds, buff, end - buff);
11108}
11109
11110
11111/*
11112 Build a list of pointer to each line in ds_input, sort
11113 the list and use the sorted list to append the strings
11114 sorted to the output ds
11115
11116 SYNOPSIS
11117 dynstr_append_sorted()
11118 ds string where the sorted output will be appended
11119 ds_input string to be sorted
11120 keep_header If header should not be sorted
11121*/
11122
11123static int comp_lines(const char **a, const char **b)
11124{
11125 return (strcmp(*a,*b));
11126}
11127
11128void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING *ds_input,
11129 bool keep_header)
11130{
11131 unsigned i;
11132 char *start= ds_input->str;
11133 DYNAMIC_ARRAY lines;
11134 DBUG_ENTER("dynstr_append_sorted");
11135
11136 if (!*start)
11137 DBUG_VOID_RETURN; /* No input */
11138
11139 my_init_dynamic_array(&lines, sizeof(const char*), 32, 32, MYF(0));
11140
11141 if (keep_header)
11142 {
11143 /* First line is result header, skip past it */
11144 while (*start && *start != '\n')
11145 start++;
11146 start++; /* Skip past \n */
11147 dynstr_append_mem(ds, ds_input->str, start - ds_input->str);
11148 }
11149
11150 /* Insert line(s) in array */
11151 while (*start)
11152 {
11153 char* line_end= (char*)start;
11154
11155 /* Find end of line */
11156 while (*line_end && *line_end != '\n')
11157 line_end++;
11158 *line_end= 0;
11159
11160 /* Insert pointer to the line in array */
11161 if (insert_dynamic(&lines, &start))
11162 die("Out of memory inserting lines to sort");
11163
11164 start= line_end+1;
11165 }
11166
11167 /* Sort array */
11168 qsort(lines.buffer, lines.elements,
11169 sizeof(char**), (qsort_cmp)comp_lines);
11170
11171 /* Create new result */
11172 for (i= 0; i < lines.elements ; i++)
11173 {
11174 const char **line= dynamic_element(&lines, i, const char**);
11175 dynstr_append(ds, *line);
11176 dynstr_append(ds, "\n");
11177 }
11178
11179 delete_dynamic(&lines);
11180 DBUG_VOID_RETURN;
11181}
11182
11183#ifndef HAVE_SETENV
11184static int setenv(const char *name, const char *value, int overwrite)
11185{
11186 size_t buflen= strlen(name) + strlen(value) + 2;
11187 char *envvar= (char *)malloc(buflen);
11188 if(!envvar)
11189 return ENOMEM;
11190 strcpy(envvar, name);
11191 strcat(envvar, "=");
11192 strcat(envvar, value);
11193 putenv(envvar);
11194 return 0;
11195}
11196#endif
11197
11198/*
11199 for the purpose of testing (see dialog.test)
11200 we replace default mysql_authentication_dialog_ask function with the one,
11201 that always reads from stdin with explicit echo.
11202
11203*/
11204MYSQL_PLUGIN_EXPORT
11205char *mysql_authentication_dialog_ask(MYSQL *mysql, int type,
11206 const char *prompt,
11207 char *buf, int buf_len)
11208{
11209 char *s=buf;
11210
11211 fputs(prompt, stdout);
11212 fputs(" ", stdout);
11213
11214 if (!fgets(buf, buf_len-1, stdin))
11215 buf[0]= 0;
11216 else if (buf[0] && (s= strend(buf))[-1] == '\n')
11217 s[-1]= 0;
11218
11219 for (s= buf; *s; s++)
11220 fputc(type == 2 ? '*' : *s, stdout);
11221
11222 fputc('\n', stdout);
11223
11224 return buf;
11225}
11226