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