1
/* Copyright (C) 2000 MySQL AB
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
19
Tool used for executing a .test file
21
See the "MySQL Test framework manual" for more information
22
http://dev.mysql.com/doc/mysqltest/en/index.html
24
Please keep the test framework tools identical in all versions!
27
Sasha Pachev <sasha@mysql.com>
28
Matt Wagner <matt@mysql.com>
34
#define MTEST_VERSION "3.3"
36
#include "client_priv.h"
37
#include <mysql_version.h>
38
#include <mysqld_error.h>
44
#include "my_regex.h" /* Our own version of regex */
45
#ifdef HAVE_SYS_WAIT_H
53
/* Use cygwin for --exec and --system before 5.0 */
54
#if MYSQL_VERSION_ID < 50000
58
#define MAX_VAR_NAME_LENGTH 256
59
#define MAX_COLUMNS 256
60
#define MAX_EMBEDDED_SERVER_ARGS 64
61
#define MAX_DELIMITER_LENGTH 16
63
/* Flags controlling send and reap */
64
#define QUERY_SEND_FLAG 1
65
#define QUERY_REAP_FLAG 2
68
OPT_SKIP_SAFEMALLOC=OPT_MAX_CLIENT_OPTION,
69
OPT_PS_PROTOCOL, OPT_SP_PROTOCOL, OPT_CURSOR_PROTOCOL, OPT_VIEW_PROTOCOL,
70
OPT_MAX_CONNECT_RETRIES, OPT_MARK_PROGRESS, OPT_LOG_DIR, OPT_TAIL_LINES
73
static int record= 0, opt_sleep= -1;
74
static char *opt_db= 0, *opt_pass= 0;
75
const char *opt_user= 0, *opt_host= 0, *unix_sock= 0, *opt_basedir= "./";
76
const char *opt_logdir= "";
77
const char *opt_include= 0, *opt_charsets_dir;
78
static int opt_port= 0;
79
static int opt_max_connect_retries;
80
static my_bool opt_compress= 0, silent= 0, verbose= 0;
81
static my_bool debug_info_flag= 0, debug_check_flag= 0;
82
static my_bool tty_password= 0;
83
static my_bool opt_mark_progress= 0;
84
static my_bool ps_protocol= 0, ps_protocol_enabled= 0;
85
static my_bool sp_protocol= 0, sp_protocol_enabled= 0;
86
static my_bool view_protocol= 0, view_protocol_enabled= 0;
87
static my_bool cursor_protocol= 0, cursor_protocol_enabled= 0;
88
static my_bool parsing_disabled= 0;
89
static my_bool display_result_vertically= FALSE,
90
display_metadata= FALSE, display_result_sorted= FALSE;
91
static my_bool disable_query_log= 0, disable_result_log= 0;
92
static my_bool disable_warnings= 0;
93
static my_bool disable_info= 1;
94
static my_bool abort_on_error= 1;
95
static my_bool server_initialized= 0;
96
static my_bool is_windows= 0;
97
static char **default_argv;
98
static const char *load_default_groups[]= { "mysqltest", "client", 0 };
99
static char line_buffer[MAX_DELIMITER_LENGTH], *line_buffer_pos= line_buffer;
101
static uint start_lineno= 0; /* Start line of current command */
102
static uint my_end_arg= 0;
104
/* Number of lines of the result to include in failure report */
105
static uint opt_tail_lines= 0;
107
static char delimiter[MAX_DELIMITER_LENGTH]= ";";
108
static uint delimiter_length= 1;
110
static char TMPDIR[FN_REFLEN];
121
int line; /* Start line of block */
122
my_bool ok; /* Should block be executed */
123
enum block_cmd cmd; /* Command owning the block */
126
static struct st_block block_stack[32];
127
static struct st_block *cur_block, *block_stack_end;
129
/* Open file stack */
133
const char *file_name;
134
uint lineno; /* Current line in file */
137
static struct st_test_file file_stack[16];
138
static struct st_test_file* cur_file;
139
static struct st_test_file* file_stack_end;
142
static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */
144
static const char *embedded_server_groups[]=
152
static int embedded_server_arg_count=0;
153
static char *embedded_server_args[MAX_EMBEDDED_SERVER_ARGS];
156
Timer related variables
157
See the timer_output() definition for details
159
static char *timer_file = NULL;
160
static ulonglong timer_start;
161
static void timer_output(void);
162
static ulonglong timer_now(void);
164
static ulonglong progress_start= 0;
166
/* Precompiled re's */
167
static my_regex_t ps_re; /* the query can be run using PS protocol */
168
static my_regex_t sp_re; /* the query can be run as a SP */
169
static my_regex_t view_re; /* the query can be run as a view*/
171
static void init_re(void);
172
static int match_re(my_regex_t *, char *);
173
static void free_re(void);
175
DYNAMIC_ARRAY q_lines;
179
int read_lines,current_line;
184
char file[FN_REFLEN];
188
/* if set, all results are concated and compared against this file */
189
const char *result_file_name= 0;
191
typedef struct st_var
199
int int_dirty; /* do not update string if int is updated until first read */
204
/*Perl/shell-like variable registers */
212
/* Used when creating views and sp, to avoid implicit commit */
217
#ifdef EMBEDDED_LIBRARY
218
const char *cur_query;
220
pthread_mutex_t mutex;
223
#endif /*EMBEDDED_LIBRARY*/
225
struct st_connection connections[128];
226
struct st_connection* cur_con= NULL, *next_con, *connections_end;
229
List of commands in mysqltest
230
Must match the "command_names" array
231
Add new commands before Q_UNKNOWN!
234
Q_CONNECTION=1, Q_QUERY,
235
Q_CONNECT, Q_SLEEP, Q_REAL_SLEEP,
237
Q_SOURCE, Q_DISCONNECT,
239
Q_WHILE, Q_END_BLOCK,
241
Q_REQUIRE, Q_SAVE_MASTER_POS,
243
Q_SYNC_SLAVE_WITH_MASTER,
246
Q_DIRTY_CLOSE, Q_REPLACE, Q_REPLACE_COLUMN,
249
Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
250
Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
251
Q_WAIT_FOR_SLAVE_TO_STOP,
252
Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
253
Q_ENABLE_INFO, Q_DISABLE_INFO,
254
Q_ENABLE_METADATA, Q_DISABLE_METADATA,
256
Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR,
257
Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
258
Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, Q_SORTED_RESULT,
259
Q_START_TIMER, Q_END_TIMER,
260
Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL,
261
Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT,
263
Q_DISABLE_PARSING, Q_ENABLE_PARSING,
264
Q_REPLACE_REGEX, Q_REMOVE_FILE, Q_FILE_EXIST,
265
Q_WRITE_FILE, Q_COPY_FILE, Q_PERL, Q_DIE, Q_EXIT, Q_SKIP,
266
Q_CHMOD_FILE, Q_APPEND_FILE, Q_CAT_FILE, Q_DIFF_FILES,
267
Q_SEND_QUIT, Q_CHANGE_USER, Q_MKDIR, Q_RMDIR,
269
Q_UNKNOWN, /* Unknown command. */
270
Q_COMMENT, /* Comments, ignored. */
271
Q_COMMENT_WITH_COMMAND
275
const char *command_names[]=
295
"sync_slave_with_master",
305
/* Enable/disable that the _query_ is logged to result file */
308
/* Enable/disable that the _result_ from a query is logged to result file */
310
"disable_result_log",
311
"wait_for_slave_to_stop",
320
"disable_abort_on_error",
321
"enable_abort_on_error",
323
"horizontal_results",
330
"disable_ps_protocol",
331
"enable_ps_protocol",
345
/* Don't execute any more commands, compare result */
362
The list of error codes to --error are stored in an internal array of
363
structs. This struct can hold numeric SQL error codes, error names or
364
SQLSTATE codes as strings. The element next to the last active element
365
in the list is set to type ERR_EMPTY. When an SQL statement returns an
366
error, we use this list to check if this is an expected error.
377
enum match_err_type type;
381
char sqlstate[SQLSTATE_LENGTH+1]; /* \0 terminated string */
385
struct st_expected_errors
387
struct st_match_err err[10];
390
static struct st_expected_errors saved_expected_errors;
394
char *query, *query_buf,*first_argument,*last_argument,*end;
395
int first_word_len, query_len;
396
my_bool abort_on_error;
397
struct st_expected_errors expected_errors;
398
char require_file[FN_REFLEN];
399
enum enum_commands type;
402
TYPELIB command_typelib= {array_elements(command_names),"",
405
DYNAMIC_STRING ds_res, ds_progress, ds_warning_messages;
407
char builtin_echo[FN_REFLEN];
409
void die(const char *fmt, ...)
410
ATTRIBUTE_FORMAT(printf, 1, 2);
411
void abort_not_supported_test(const char *fmt, ...)
412
ATTRIBUTE_FORMAT(printf, 1, 2);
413
void verbose_msg(const char *fmt, ...)
414
ATTRIBUTE_FORMAT(printf, 1, 2);
415
void warning_msg(const char *fmt, ...)
416
ATTRIBUTE_FORMAT(printf, 1, 2);
417
void log_msg(const char *fmt, ...)
418
ATTRIBUTE_FORMAT(printf, 1, 2);
420
VAR* var_from_env(const char *, const char *);
421
VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
423
void var_free(void* v);
424
VAR* var_get(const char *var_name, const char** var_name_end,
425
my_bool raw, my_bool ignore_not_existing);
426
void eval_expr(VAR* v, const char *p, const char** p_end);
427
my_bool match_delimiter(int c, const char *delim, uint length);
428
void dump_result_to_reject_file(char *buf, int size);
429
void dump_result_to_log_file(char *buf, int size);
430
void dump_warning_messages();
431
void dump_progress();
433
void do_eval(DYNAMIC_STRING *query_eval, const char *query,
434
const char *query_end, my_bool pass_through_escape_chars);
435
void str_to_file(const char *fname, char *str, int size);
436
void str_to_file2(const char *fname, char *str, int size, my_bool append);
439
void free_tmp_sh_file();
440
void free_win_path_patterns();
444
/* For replace_column */
445
static char *replace_column[MAX_COLUMNS];
446
static uint max_replace_column= 0;
447
void do_get_replace_column(struct st_command*);
448
void free_replace_column();
451
void do_get_replace(struct st_command *command);
454
/* For replace_regex */
455
void do_get_replace_regex(struct st_command *command);
456
void free_replace_regex();
459
void free_all_replace(){
461
free_replace_regex();
462
free_replace_column();
465
void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
467
void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val);
468
void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val);
469
void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING* ds_input);
471
void handle_error(struct st_command*,
472
unsigned int err_errno, const char *err_error,
473
const char *err_sqlstate, DYNAMIC_STRING *ds);
474
void handle_no_error(struct st_command*);
476
#ifdef EMBEDDED_LIBRARY
478
/* attributes of the query thread */
479
pthread_attr_t cn_thd_attrib;
482
send_one_query executes query in separate thread, which is
483
necessary in embedded library to run 'send' in proper way.
484
This implementation doesn't handle errors returned
485
by mysql_send_query. It's technically possible, though
486
I don't see where it is needed.
488
pthread_handler_t send_one_query(void *arg)
490
struct st_connection *cn= (struct st_connection*)arg;
493
VOID(mysql_send_query(&cn->mysql, cn->cur_query, cn->cur_query_len));
496
pthread_mutex_lock(&cn->mutex);
498
VOID(pthread_cond_signal(&cn->cond));
499
pthread_mutex_unlock(&cn->mutex);
504
static int do_send_query(struct st_connection *cn, const char *q, int q_len,
509
if (flags & QUERY_REAP_FLAG)
510
return mysql_send_query(&cn->mysql, q, q_len);
512
if (pthread_mutex_init(&cn->mutex, NULL) ||
513
pthread_cond_init(&cn->cond, NULL))
514
die("Error in the thread library");
517
cn->cur_query_len= q_len;
519
if (pthread_create(&tid, &cn_thd_attrib, send_one_query, (void*)cn))
520
die("Cannot start new thread for query");
525
static void wait_query_thread_end(struct st_connection *con)
527
if (!con->query_done)
529
pthread_mutex_lock(&con->mutex);
530
while (!con->query_done)
531
pthread_cond_wait(&con->cond, &con->mutex);
532
pthread_mutex_unlock(&con->mutex);
536
#else /*EMBEDDED_LIBRARY*/
538
#define do_send_query(cn,q,q_len,flags) mysql_send_query(&cn->mysql, q, q_len)
540
#endif /*EMBEDDED_LIBRARY*/
542
void do_eval(DYNAMIC_STRING *query_eval, const char *query,
543
const char *query_end, my_bool pass_through_escape_chars)
546
register char c, next_c;
547
register int escaped = 0;
549
DBUG_ENTER("do_eval");
551
for (p= query; (c= *p) && p < query_end; ++p)
558
dynstr_append_mem(query_eval, p, 1);
562
if (!(v= var_get(p, &p, 0, 0)))
563
die("Bad variable in eval");
564
dynstr_append_mem(query_eval, v->str_val, v->str_val_len);
572
dynstr_append_mem(query_eval, p, 1);
574
else if (next_c == '\\' || next_c == '$' || next_c == '"')
576
/* Set escaped only if next char is \, " or $ */
579
if (pass_through_escape_chars)
581
/* The escape char should be added to the output string. */
582
dynstr_append_mem(query_eval, p, 1);
586
dynstr_append_mem(query_eval, p, 1);
590
dynstr_append_mem(query_eval, p, 1);
599
Run query and dump the result to stdout in vertical format
601
NOTE! This function should be safe to call when an error
602
has occured and thus any further errors will be ignored(although logged)
606
mysql - connection to use
611
static void show_query(MYSQL* mysql, const char* query)
614
DBUG_ENTER("show_query");
619
if (mysql_query(mysql, query))
621
log_msg("Error running query '%s': %d %s",
622
query, mysql_errno(mysql), mysql_error(mysql));
626
if ((res= mysql_store_result(mysql)) == NULL)
628
/* No result set returned */
635
unsigned int row_num= 0;
636
unsigned int num_fields= mysql_num_fields(res);
637
MYSQL_FIELD *fields= mysql_fetch_fields(res);
639
fprintf(stderr, "=== %s ===\n", query);
640
while ((row= mysql_fetch_row(res)))
642
unsigned long *lengths= mysql_fetch_lengths(res);
645
fprintf(stderr, "---- %d. ----\n", row_num);
646
for(i= 0; i < num_fields; i++)
648
fprintf(stderr, "%s\t%.*s\n",
650
(int)lengths[i], row[i] ? row[i] : "NULL");
653
for (i= 0; i < strlen(query)+8; i++)
654
fprintf(stderr, "=");
655
fprintf(stderr, "\n\n");
657
mysql_free_result(res);
664
Show any warnings just before the error. Since the last error
665
is added to the warning stack, only print @@warning_count-1 warnings.
667
NOTE! This function should be safe to call when an error
668
has occured and this any further errors will be ignored(although logged)
671
show_warnings_before_error
672
mysql - connection to use
676
static void show_warnings_before_error(MYSQL* mysql)
679
const char* query= "SHOW WARNINGS";
680
DBUG_ENTER("show_warnings_before_error");
685
if (mysql_query(mysql, query))
687
log_msg("Error running query '%s': %d %s",
688
query, mysql_errno(mysql), mysql_error(mysql));
692
if ((res= mysql_store_result(mysql)) == NULL)
694
/* No result set returned */
698
if (mysql_num_rows(res) <= 1)
700
/* Don't display the last row, it's "last error" */
705
unsigned int row_num= 0;
706
unsigned int num_fields= mysql_num_fields(res);
708
fprintf(stderr, "\nWarnings from just before the error:\n");
709
while ((row= mysql_fetch_row(res)))
712
unsigned long *lengths= mysql_fetch_lengths(res);
714
if (++row_num >= mysql_num_rows(res))
716
/* Don't display the last row, it's "last error" */
720
for(i= 0; i < num_fields; i++)
722
fprintf(stderr, "%.*s ", (int)lengths[i],
723
row[i] ? row[i] : "NULL");
725
fprintf(stderr, "\n");
728
mysql_free_result(res);
741
const char *argname; /* Name of argument */
742
enum arg_type type; /* Type of argument */
743
my_bool required; /* Argument required */
744
DYNAMIC_STRING *ds; /* Storage for argument */
745
const char *description; /* Description of the argument */
749
void check_command_args(struct st_command *command,
750
const char *arguments,
751
const struct command_arg *args,
752
int num_args, const char delimiter_arg)
755
const char *ptr= arguments;
757
DBUG_ENTER("check_command_args");
758
DBUG_PRINT("enter", ("num_args: %d", num_args));
760
for (i= 0; i < num_args; i++)
762
const struct command_arg *arg= &args[i];
767
/* Skip leading spaces */
768
while (*ptr && *ptr == ' ')
771
/* Find end of arg, terminated by "delimiter_arg" */
772
while (*ptr && *ptr != delimiter_arg)
776
init_dynamic_string(arg->ds, 0, ptr-start, 32);
777
do_eval(arg->ds, start, ptr, FALSE);
782
init_dynamic_string(arg->ds, "", 0, 0);
784
command->last_argument= (char*)ptr;
786
/* Step past the delimiter */
787
if (*ptr && *ptr == delimiter_arg)
789
DBUG_PRINT("info", ("val: %s", arg->ds->str));
795
init_dynamic_string(arg->ds, 0, command->query_len, 256);
796
do_eval(arg->ds, start, command->end, FALSE);
797
command->last_argument= command->end;
798
DBUG_PRINT("info", ("val: %s", arg->ds->str));
802
DBUG_ASSERT("Unknown argument type");
806
/* Check required arg */
807
if (arg->ds->length == 0 && arg->required)
808
die("Missing required argument '%s' to command '%.*s'", arg->argname,
809
command->first_word_len, command->query);
812
/* Check for too many arguments passed */
813
ptr= command->last_argument;
814
while(ptr <= command->end)
816
if (*ptr && *ptr != ' ')
817
die("Extra argument '%s' passed to '%.*s'",
818
ptr, command->first_word_len, command->query);
825
void handle_command_error(struct st_command *command, uint error)
827
DBUG_ENTER("handle_command_error");
828
DBUG_PRINT("enter", ("error: %d", error));
833
if (command->abort_on_error)
834
die("command \"%.*s\" failed with error %d",
835
command->first_word_len, command->query, error);
836
for (i= 0; i < command->expected_errors.count; i++)
838
DBUG_PRINT("info", ("expected error: %d",
839
command->expected_errors.err[i].code.errnum));
840
if ((command->expected_errors.err[i].type == ERR_ERRNO) &&
841
(command->expected_errors.err[i].code.errnum == error))
843
DBUG_PRINT("info", ("command \"%.*s\" failed with expected error: %d",
844
command->first_word_len, command->query, error));
848
die("command \"%.*s\" failed with wrong error: %d",
849
command->first_word_len, command->query, error);
851
else if (command->expected_errors.err[0].type == ERR_ERRNO &&
852
command->expected_errors.err[0].code.errnum != 0)
854
/* Error code we wanted was != 0, i.e. not an expected success */
855
die("command \"%.*s\" succeeded - should have failed with errno %d...",
856
command->first_word_len, command->query,
857
command->expected_errors.err[0].code.errnum);
863
void close_connections()
865
DBUG_ENTER("close_connections");
866
for (--next_con; next_con >= connections; --next_con)
869
mysql_stmt_close(next_con->stmt);
871
mysql_close(&next_con->mysql);
872
if (next_con->util_mysql)
873
mysql_close(next_con->util_mysql);
874
my_free(next_con->name, MYF(MY_ALLOW_ZERO_PTR));
880
void close_statements()
882
struct st_connection *con;
883
DBUG_ENTER("close_statements");
884
for (con= connections; con < next_con; con++)
887
mysql_stmt_close(con->stmt);
896
DBUG_ENTER("close_files");
897
for (; cur_file >= file_stack; cur_file--)
899
if (cur_file->file && cur_file->file != stdin)
901
DBUG_PRINT("info", ("closing file: %s", cur_file->file_name));
902
my_fclose(cur_file->file, MYF(0));
904
my_free((uchar*) cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR));
905
cur_file->file_name= 0;
911
void free_used_memory()
914
DBUG_ENTER("free_used_memory");
918
hash_free(&var_hash);
920
for (i= 0 ; i < q_lines.elements ; i++)
922
struct st_command **q= dynamic_element(&q_lines, i, struct st_command**);
923
my_free((*q)->query_buf,MYF(MY_ALLOW_ZERO_PTR));
924
my_free((*q),MYF(0));
926
for (i= 0; i < 10; i++)
928
if (var_reg[i].alloced_len)
929
my_free(var_reg[i].str_val, MYF(MY_WME));
931
while (embedded_server_arg_count > 1)
932
my_free(embedded_server_args[--embedded_server_arg_count],MYF(0));
933
delete_dynamic(&q_lines);
934
dynstr_free(&ds_res);
935
dynstr_free(&ds_progress);
936
dynstr_free(&ds_warning_messages);
938
my_free(opt_pass,MYF(MY_ALLOW_ZERO_PTR));
939
free_defaults(default_argv);
943
free_win_path_patterns();
946
/* Only call mysql_server_end if mysql_server_init has been called */
947
if (server_initialized)
950
/* Don't use DBUG after mysql_server_end() */
955
static void cleanup_and_exit(int exit_code)
972
printf("unknown exit code: %d\n", exit_code);
980
void die(const char *fmt, ...)
985
DBUG_PRINT("enter", ("start_lineno: %d", start_lineno));
988
Protect against dying twice
989
first time 'die' is called, try to write log files
990
second time, just exit
996
/* Print the error message */
997
fprintf(stderr, "mysqltest: ");
998
if (cur_file && cur_file != file_stack)
999
fprintf(stderr, "In included file \"%s\": ",
1000
cur_file->file_name);
1001
if (start_lineno > 0)
1002
fprintf(stderr, "At line %u: ", start_lineno);
1005
va_start(args, fmt);
1006
vfprintf(stderr, fmt, args);
1010
fprintf(stderr, "unknown error");
1011
fprintf(stderr, "\n");
1014
/* Show results from queries just before failure */
1015
if (ds_res.length && opt_tail_lines)
1017
int tail_lines= opt_tail_lines;
1018
char* show_from= ds_res.str + ds_res.length - 1;
1019
while(show_from > ds_res.str && tail_lines > 0 )
1022
if (*show_from == '\n')
1025
fprintf(stderr, "\nThe result from queries just before the failure was:\n");
1026
if (show_from > ds_res.str)
1027
fprintf(stderr, "< snip >");
1028
fprintf(stderr, "%s", show_from);
1032
/* Dump the result that has been accumulated so far to .log file */
1033
if (result_file_name && ds_res.length)
1034
dump_result_to_log_file(ds_res.str, ds_res.length);
1036
/* Dump warning messages */
1037
if (result_file_name && ds_warning_messages.length)
1038
dump_warning_messages();
1041
Help debugging by displaying any warnings that might have
1042
been produced prior to the error
1045
show_warnings_before_error(&cur_con->mysql);
1047
cleanup_and_exit(1);
1051
void abort_not_supported_test(const char *fmt, ...)
1054
struct st_test_file* err_file= cur_file;
1055
DBUG_ENTER("abort_not_supported_test");
1057
/* Print include filestack */
1058
fprintf(stderr, "The test '%s' is not supported by this installation\n",
1059
file_stack->file_name);
1060
fprintf(stderr, "Detected in file %s at line %d\n",
1061
err_file->file_name, err_file->lineno);
1062
while (err_file != file_stack)
1065
fprintf(stderr, "included from %s at line %d\n",
1066
err_file->file_name, err_file->lineno);
1069
/* Print error message */
1070
va_start(args, fmt);
1073
fprintf(stderr, "reason: ");
1074
vfprintf(stderr, fmt, args);
1075
fprintf(stderr, "\n");
1080
cleanup_and_exit(62);
1084
void abort_not_in_this_version()
1086
die("Not available in this version of mysqltest");
1090
void deprecated(struct st_command *command)
1092
die("'%.*s'has been deprecated",
1093
command->first_word_len, command->query);
1097
void verbose_msg(const char *fmt, ...)
1100
DBUG_ENTER("verbose_msg");
1104
va_start(args, fmt);
1105
fprintf(stderr, "mysqltest: ");
1106
if (cur_file && cur_file != file_stack)
1107
fprintf(stderr, "In included file \"%s\": ",
1108
cur_file->file_name);
1109
if (start_lineno != 0)
1110
fprintf(stderr, "At line %u: ", start_lineno);
1111
vfprintf(stderr, fmt, args);
1112
fprintf(stderr, "\n");
1119
void warning_msg(const char *fmt, ...)
1124
DBUG_ENTER("warning_msg");
1126
va_start(args, fmt);
1127
dynstr_append(&ds_warning_messages, "mysqltest: ");
1128
if (start_lineno != 0)
1130
dynstr_append(&ds_warning_messages, "Warning detected ");
1131
if (cur_file && cur_file != file_stack)
1133
len= my_snprintf(buff, sizeof(buff), "in included file %s ",
1134
cur_file->file_name);
1135
dynstr_append_mem(&ds_warning_messages,
1138
len= my_snprintf(buff, sizeof(buff), "at line %d: ",
1140
dynstr_append_mem(&ds_warning_messages,
1144
len= my_vsnprintf(buff, sizeof(buff), fmt, args);
1145
dynstr_append_mem(&ds_warning_messages, buff, len);
1147
dynstr_append(&ds_warning_messages, "\n");
1154
void log_msg(const char *fmt, ...)
1159
DBUG_ENTER("log_msg");
1161
va_start(args, fmt);
1162
len= my_vsnprintf(buff, sizeof(buff)-1, fmt, args);
1165
dynstr_append_mem(&ds_res, buff, len);
1166
dynstr_append(&ds_res, "\n");
1173
Read a file and append it to ds
1177
ds - pointer to dynamic string where to add the files content
1178
filename - name of the file to read
1182
void cat_file(DYNAMIC_STRING* ds, const char* filename)
1188
if ((fd= my_open(filename, O_RDONLY, MYF(0))) < 0)
1189
die("Failed to open file '%s'", filename);
1190
while((len= my_read(fd, (uchar*)&buff,
1191
sizeof(buff), MYF(0))) > 0)
1193
char *p= buff, *start= buff;
1194
while (p < buff+len)
1196
/* Convert cr/lf to lf */
1197
if (*p == '\r' && *(p+1) && *(p+1)== '\n')
1199
/* Add fake newline instead of cr and output the line */
1201
p++; /* Step past the "fake" newline */
1202
dynstr_append_mem(ds, start, p-start);
1203
p++; /* Step past the "fake" newline */
1209
/* Output any chars that migh be left */
1210
dynstr_append_mem(ds, start, p-start);
1212
my_close(fd, MYF(0));
1217
Run the specified command with popen
1221
cmd - command to execute(should be properly quoted
1222
ds_res- pointer to dynamic string where to store the result
1226
static int run_command(char* cmd,
1227
DYNAMIC_STRING *ds_res)
1233
if (!(res_file= popen(cmd, "r")))
1234
die("popen(\"%s\", \"r\") failed", cmd);
1236
while (fgets(buf, sizeof(buf), res_file))
1238
DBUG_PRINT("info", ("buf: %s", buf));
1241
/* Save the output of this command in the supplied string */
1242
dynstr_append(ds_res, buf);
1246
/* Print it directly on screen */
1247
fprintf(stdout, "%s", buf);
1251
error= pclose(res_file);
1252
return WEXITSTATUS(error);
1257
Run the specified tool with variable number of arguments
1261
tool_path - the name of the tool to run
1262
ds_res - pointer to dynamic string where to store the result
1263
... - variable number of arguments that will be properly
1264
quoted and appended after the tool's name
1268
static int run_tool(const char *tool_path, DYNAMIC_STRING *ds_res, ...)
1273
DYNAMIC_STRING ds_cmdline;
1275
DBUG_ENTER("run_tool");
1276
DBUG_PRINT("enter", ("tool_path: %s", tool_path));
1278
if (init_dynamic_string(&ds_cmdline, IF_WIN("\"", ""), FN_REFLEN, FN_REFLEN))
1279
die("Out of memory");
1281
dynstr_append_os_quoted(&ds_cmdline, tool_path, NullS);
1282
dynstr_append(&ds_cmdline, " ");
1284
va_start(args, ds_res);
1286
while ((arg= va_arg(args, char *)))
1288
/* Options should be os quoted */
1289
if (strncmp(arg, "--", 2) == 0)
1290
dynstr_append_os_quoted(&ds_cmdline, arg, NullS);
1292
dynstr_append(&ds_cmdline, arg);
1293
dynstr_append(&ds_cmdline, " ");
1299
dynstr_append(&ds_cmdline, "\"");
1302
DBUG_PRINT("info", ("Running: %s", ds_cmdline.str));
1303
ret= run_command(ds_cmdline.str, ds_res);
1304
DBUG_PRINT("exit", ("ret: %d", ret));
1305
dynstr_free(&ds_cmdline);
1311
Show the diff of two files using the systems builtin diff
1312
command. If no such diff command exist, just dump the content
1313
of the two files and inform about how to get "diff"
1317
ds - pointer to dynamic string where to add the diff(may be NULL)
1318
filename1 - name of first file
1319
filename2 - name of second file
1323
void show_diff(DYNAMIC_STRING* ds,
1324
const char* filename1, const char* filename2)
1327
DYNAMIC_STRING ds_tmp;
1329
if (init_dynamic_string(&ds_tmp, "", 256, 256))
1330
die("Out of memory");
1332
/* First try with unified diff */
1333
if (run_tool("diff",
1334
&ds_tmp, /* Get output from diff in ds_tmp */
1339
NULL) > 1) /* Most "diff" tools return >1 if error */
1341
dynstr_set(&ds_tmp, "");
1343
/* Fallback to context diff with "diff -c" */
1344
if (run_tool("diff",
1345
&ds_tmp, /* Get output from diff in ds_tmp */
1350
NULL) > 1) /* Most "diff" tools return >1 if error */
1353
Fallback to dump both files to result file and inform
1354
about installing "diff"
1356
dynstr_set(&ds_tmp, "");
1358
dynstr_append(&ds_tmp,
1360
"The two files differ but it was not possible to execute 'diff' in\n"
1361
"order to show only the difference, tried both 'diff -u' or 'diff -c'.\n"
1362
"Instead the whole content of the two files was shown for you to diff manually. ;)\n\n"
1363
"To get a better report you should install 'diff' on your system, which you\n"
1364
"for example can get from http://www.gnu.org/software/diffutils/diffutils.html\n"
1366
"or http://gnuwin32.sourceforge.net/packages/diffutils.htm\n"
1370
dynstr_append(&ds_tmp, " --- ");
1371
dynstr_append(&ds_tmp, filename1);
1372
dynstr_append(&ds_tmp, " >>>\n");
1373
cat_file(&ds_tmp, filename1);
1374
dynstr_append(&ds_tmp, "<<<\n --- ");
1375
dynstr_append(&ds_tmp, filename1);
1376
dynstr_append(&ds_tmp, " >>>\n");
1377
cat_file(&ds_tmp, filename2);
1378
dynstr_append(&ds_tmp, "<<<<\n");
1384
/* Add the diff to output */
1385
dynstr_append_mem(ds, ds_tmp.str, ds_tmp.length);
1389
/* Print diff directly to stdout */
1390
fprintf(stderr, "%s\n", ds_tmp.str);
1393
dynstr_free(&ds_tmp);
1398
enum compare_files_result_enum {
1400
RESULT_CONTENT_MISMATCH= 1,
1401
RESULT_LENGTH_MISMATCH= 2
1405
Compare two files, given a fd to the first file and
1406
name of the second file
1410
fd - Open file descriptor of the first file
1411
filename2 - Name of second file
1414
According to the values in "compare_files_result_enum"
1418
int compare_files2(File fd, const char* filename2)
1420
int error= RESULT_OK;
1423
char buff[512], buff2[512];
1425
if ((fd2= my_open(filename2, O_RDONLY, MYF(0))) < 0)
1427
my_close(fd, MYF(0));
1428
die("Failed to open second file: '%s'", filename2);
1430
while((len= my_read(fd, (uchar*)&buff,
1431
sizeof(buff), MYF(0))) > 0)
1433
if ((len2= my_read(fd2, (uchar*)&buff2,
1434
sizeof(buff2), MYF(0))) < len)
1436
/* File 2 was smaller */
1437
error= RESULT_LENGTH_MISMATCH;
1442
/* File 1 was smaller */
1443
error= RESULT_LENGTH_MISMATCH;
1446
if ((memcmp(buff, buff2, len)))
1448
/* Content of this part differed */
1449
error= RESULT_CONTENT_MISMATCH;
1453
if (!error && my_read(fd2, (uchar*)&buff2,
1454
sizeof(buff2), MYF(0)) > 0)
1456
/* File 1 was smaller */
1457
error= RESULT_LENGTH_MISMATCH;
1460
my_close(fd2, MYF(0));
1467
Compare two files, given their filenames
1471
filename1 - Name of first file
1472
filename2 - Name of second file
1475
See 'compare_files2'
1479
int compare_files(const char* filename1, const char* filename2)
1484
if ((fd= my_open(filename1, O_RDONLY, MYF(0))) < 0)
1485
die("Failed to open first file: '%s'", filename1);
1487
error= compare_files2(fd, filename2);
1489
my_close(fd, MYF(0));
1496
Compare content of the string in ds to content of file fname
1500
ds - Dynamic string containing the string o be compared
1501
fname - Name of file to compare with
1504
See 'compare_files2'
1507
int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname)
1511
char temp_file_path[FN_REFLEN];
1513
DBUG_ENTER("dyn_string_cmp");
1514
DBUG_PRINT("enter", ("fname: %s", fname));
1516
if ((fd= create_temp_file(temp_file_path, NULL,
1517
"tmp", O_CREAT | O_SHARE | O_RDWR,
1519
die("Failed to create temporary file for ds");
1521
/* Write ds to temporary file and set file pos to beginning*/
1522
if (my_write(fd, (uchar *) ds->str, ds->length,
1523
MYF(MY_FNABP | MY_WME)) ||
1524
my_seek(fd, 0, SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR)
1526
my_close(fd, MYF(0));
1527
/* Remove the temporary file */
1528
my_delete(temp_file_path, MYF(0));
1529
die("Failed to write file '%s'", temp_file_path);
1532
error= compare_files2(fd, fname);
1534
my_close(fd, MYF(0));
1535
/* Remove the temporary file */
1536
my_delete(temp_file_path, MYF(0));
1543
Check the content of ds against result file
1547
ds - content to be checked
1550
error - the function will not return
1554
void check_result(DYNAMIC_STRING* ds)
1556
const char* mess= "Result content mismatch\n";
1558
DBUG_ENTER("check_result");
1559
DBUG_ASSERT(result_file_name);
1560
DBUG_PRINT("enter", ("result_file_name: %s", result_file_name));
1562
if (access(result_file_name, F_OK) != 0)
1563
die("The specified result file does not exist: '%s'", result_file_name);
1565
switch (dyn_string_cmp(ds, result_file_name)) {
1568
case RESULT_LENGTH_MISMATCH:
1569
mess= "Result length mismatch\n";
1571
case RESULT_CONTENT_MISMATCH:
1574
Result mismatched, dump results to .reject file
1575
and then show the diff
1577
char reject_file[FN_REFLEN];
1578
size_t reject_length;
1579
dirname_part(reject_file, result_file_name, &reject_length);
1581
if (access(reject_file, W_OK) == 0)
1583
/* Result file directory is writable, save reject file there */
1584
fn_format(reject_file, result_file_name, NULL,
1585
".reject", MY_REPLACE_EXT);
1589
/* Put reject file in opt_logdir */
1590
fn_format(reject_file, result_file_name, opt_logdir,
1591
".reject", MY_REPLACE_DIR | MY_REPLACE_EXT);
1593
str_to_file(reject_file, ds->str, ds->length);
1595
dynstr_set(ds, NULL); /* Don't create a .log file */
1597
show_diff(NULL, result_file_name, reject_file);
1601
default: /* impossible */
1602
die("Unknown error code from dyn_string_cmp()");
1610
Check the content of ds against a require file
1611
If match fails, abort the test with special error code
1612
indicating that test is not supported
1616
ds - content to be checked
1617
fname - name of file to check against
1620
error - the function will not return
1624
void check_require(DYNAMIC_STRING* ds, const char *fname)
1626
DBUG_ENTER("check_require");
1628
if (dyn_string_cmp(ds, fname))
1630
char reason[FN_REFLEN];
1631
fn_format(reason, fname, "", "", MY_REPLACE_EXT | MY_REPLACE_DIR);
1632
abort_not_supported_test("Test requires: '%s'", reason);
1639
Remove surrounding chars from string
1641
Return 1 if first character is found but not last
1643
static int strip_surrounding(char* str, char c1, char c2)
1647
/* Check if the first non space character is c1 */
1648
while(*ptr && my_isspace(charset_info, *ptr))
1652
/* Replace it with a space */
1655
/* Last non space charecter should be c2 */
1657
while(*ptr && my_isspace(charset_info, *ptr))
1661
/* Replace it with \0 */
1666
/* Mismatch detected */
1674
static void strip_parentheses(struct st_command *command)
1676
if (strip_surrounding(command->first_argument, '(', ')'))
1677
die("%.*s - argument list started with '%c' must be ended with '%c'",
1678
command->first_word_len, command->query, '(', ')');
1682
static uchar *get_var_key(const uchar* var, size_t *len,
1683
my_bool __attribute__((unused)) t)
1686
key = ((VAR*)var)->name;
1687
*len = ((VAR*)var)->name_len;
1692
VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
1697
if (!name_len && name)
1698
name_len = strlen(name);
1699
if (!val_len && val)
1700
val_len = strlen(val) ;
1701
val_alloc_len = val_len + 16; /* room to grow */
1702
if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var)
1703
+ name_len+1, MYF(MY_WME))))
1704
die("Out of memory");
1706
tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0;
1707
tmp_var->alloced = (v == 0);
1709
if (!(tmp_var->str_val = my_malloc(val_alloc_len+1, MYF(MY_WME))))
1710
die("Out of memory");
1712
memcpy(tmp_var->name, name, name_len);
1715
memcpy(tmp_var->str_val, val, val_len);
1716
tmp_var->str_val[val_len]= 0;
1718
tmp_var->name_len = name_len;
1719
tmp_var->str_val_len = val_len;
1720
tmp_var->alloced_len = val_alloc_len;
1721
tmp_var->int_val = (val) ? atoi(val) : 0;
1722
tmp_var->int_dirty = 0;
1728
void var_free(void *v)
1730
my_free(((VAR*) v)->str_val, MYF(MY_WME));
1731
my_free(((VAR*) v)->env_s, MYF(MY_WME|MY_ALLOW_ZERO_PTR));
1732
if (((VAR*)v)->alloced)
1733
my_free(v, MYF(MY_WME));
1737
VAR* var_from_env(const char *name, const char *def_val)
1741
if (!(tmp = getenv(name)))
1744
v = var_init(0, name, strlen(name), tmp, strlen(tmp));
1745
my_hash_insert(&var_hash, (uchar*)v);
1750
VAR* var_get(const char *var_name, const char **var_name_end, my_bool raw,
1751
my_bool ignore_not_existing)
1755
DBUG_ENTER("var_get");
1756
DBUG_PRINT("enter", ("var_name: %s",var_name));
1758
if (*var_name != '$')
1760
digit = *++var_name - '0';
1761
if (digit < 0 || digit >= 10)
1763
const char *save_var_name = var_name, *end;
1765
end = (var_name_end) ? *var_name_end : 0;
1766
while (my_isvar(charset_info,*var_name) && var_name != end)
1768
if (var_name == save_var_name)
1770
if (ignore_not_existing)
1772
die("Empty variable");
1774
length= (uint) (var_name - save_var_name);
1775
if (length >= MAX_VAR_NAME_LENGTH)
1776
die("Too long variable name: %s", save_var_name);
1778
if (!(v = (VAR*) hash_search(&var_hash, (const uchar*) save_var_name,
1781
char buff[MAX_VAR_NAME_LENGTH+1];
1782
strmake(buff, save_var_name, length);
1783
v= var_from_env(buff, "");
1785
var_name--; /* Point at last character */
1788
v = var_reg + digit;
1790
if (!raw && v->int_dirty)
1792
sprintf(v->str_val, "%d", v->int_val);
1794
v->str_val_len = strlen(v->str_val);
1797
*var_name_end = var_name ;
1802
die("Unsupported variable name: %s", var_name);
1807
VAR *var_obtain(const char *name, int len)
1810
if ((v = (VAR*)hash_search(&var_hash, (const uchar *) name, len)))
1812
v = var_init(0, name, len, "", 0);
1813
my_hash_insert(&var_hash, (uchar*)v);
1819
- if variable starts with a $ it is regarded as a local test varable
1820
- if not it is treated as a environment variable, and the corresponding
1821
environment variable will be updated
1824
void var_set(const char *var_name, const char *var_name_end,
1825
const char *var_val, const char *var_val_end)
1827
int digit, env_var= 0;
1829
DBUG_ENTER("var_set");
1830
DBUG_PRINT("enter", ("var_name: '%.*s' = '%.*s' (length: %d)",
1831
(int) (var_name_end - var_name), var_name,
1832
(int) (var_val_end - var_val), var_val,
1833
(int) (var_val_end - var_val)));
1835
if (*var_name != '$')
1840
digit= *var_name - '0';
1841
if (!(digit < 10 && digit >= 0))
1843
v= var_obtain(var_name, (uint) (var_name_end - var_name));
1848
eval_expr(v, var_val, (const char**) &var_val_end);
1852
char buf[1024], *old_env_s= v->env_s;
1855
sprintf(v->str_val, "%d", v->int_val);
1857
v->str_val_len= strlen(v->str_val);
1859
my_snprintf(buf, sizeof(buf), "%.*s=%.*s",
1860
v->name_len, v->name,
1861
v->str_val_len, v->str_val);
1862
if (!(v->env_s= my_strdup(buf, MYF(MY_WME))))
1863
die("Out of memory");
1865
my_free(old_env_s, MYF(MY_ALLOW_ZERO_PTR));
1871
void var_set_string(const char* name, const char* value)
1873
var_set(name, name + strlen(name), value, value + strlen(value));
1877
void var_set_int(const char* name, int value)
1880
my_snprintf(buf, sizeof(buf), "%d", value);
1881
var_set_string(name, buf);
1886
Store an integer (typically the returncode of the last SQL)
1887
statement in the mysqltest builtin variable $mysql_errno
1890
void var_set_errno(int sql_errno)
1892
var_set_int("$mysql_errno", sql_errno);
1897
Update $mysql_get_server_version variable with version
1898
of the currently connected server
1901
void var_set_mysql_get_server_version(MYSQL* mysql)
1903
var_set_int("$mysql_get_server_version", mysql_get_server_version(mysql));
1908
Set variable from the result of a query
1912
var variable to set from query
1913
query start of query string to execute
1914
query_end end of the query string to execute
1918
let @<var_name> = `<query>`
1920
Execute the query and assign the first row of result to var as
1921
a tab separated strings
1923
Also assign each column of the result set to
1924
variable "$<var_name>_<column_name>"
1925
Thus the tab separated output can be read from $<var_name> and
1926
and each individual column can be read as $<var_name>_<col_name>
1930
void var_query_set(VAR *var, const char *query, const char** query_end)
1932
char *end = (char*)((query_end && *query_end) ?
1933
*query_end : query + strlen(query));
1936
MYSQL* mysql = &cur_con->mysql;
1937
DYNAMIC_STRING ds_query;
1938
DBUG_ENTER("var_query_set");
1940
while (end > query && *end != '`')
1943
die("Syntax error in query, missing '`'");
1946
/* Eval the query, thus replacing all environment variables */
1947
init_dynamic_string(&ds_query, 0, (end - query) + 32, 256);
1948
do_eval(&ds_query, query, end, FALSE);
1950
if (mysql_real_query(mysql, ds_query.str, ds_query.length))
1951
die("Error running query '%s': %d %s", ds_query.str,
1952
mysql_errno(mysql), mysql_error(mysql));
1953
if (!(res= mysql_store_result(mysql)))
1954
die("Query '%s' didn't return a result set", ds_query.str);
1955
dynstr_free(&ds_query);
1957
if ((row= mysql_fetch_row(res)) && row[0])
1960
Concatenate all fields in the first row with tab in between
1961
and assign that string to the $variable
1963
DYNAMIC_STRING result;
1967
init_dynamic_string(&result, "", 512, 512);
1968
lengths= mysql_fetch_lengths(res);
1969
for (i= 0; i < mysql_num_fields(res); i++)
1973
/* Add column to tab separated string */
1974
dynstr_append_mem(&result, row[i], lengths[i]);
1976
dynstr_append_mem(&result, "\t", 1);
1978
end= result.str + result.length-1;
1979
eval_expr(var, result.str, (const char**) &end);
1980
dynstr_free(&result);
1983
eval_expr(var, "", 0);
1985
mysql_free_result(res);
1991
Set variable from the result of a field in a query
1993
This function is useful when checking for a certain value
1994
in the output from a query that can't be restricted to only
1995
return some values. A very good example of that is most SHOW
1999
var_set_query_get_value()
2002
let $variable= query_get_value(<query to run>,<column name>,<row no>);
2004
<query to run> - The query that should be sent to the server
2005
<column name> - Name of the column that holds the field be compared
2006
against the expected value
2007
<row no> - Number of the row that holds the field to be
2008
compared against the expected value
2012
void var_set_query_get_value(struct st_command *command, VAR *var)
2017
MYSQL* mysql= &cur_con->mysql;
2019
static DYNAMIC_STRING ds_query;
2020
static DYNAMIC_STRING ds_col;
2021
static DYNAMIC_STRING ds_row;
2022
const struct command_arg query_get_value_args[] = {
2023
"query", ARG_STRING, TRUE, &ds_query, "Query to run",
2024
"column name", ARG_STRING, TRUE, &ds_col, "Name of column",
2025
"row number", ARG_STRING, TRUE, &ds_row, "Number for row"
2028
DBUG_ENTER("var_set_query_get_value");
2030
strip_parentheses(command);
2031
DBUG_PRINT("info", ("query: %s", command->query));
2032
check_command_args(command, command->first_argument, query_get_value_args,
2033
sizeof(query_get_value_args)/sizeof(struct command_arg),
2036
DBUG_PRINT("info", ("query: %s", ds_query.str));
2037
DBUG_PRINT("info", ("col: %s", ds_col.str));
2039
/* Convert row number to int */
2040
if (!str2int(ds_row.str, 10, (long) 0, (long) INT_MAX, &row_no))
2041
die("Invalid row number: '%s'", ds_row.str);
2042
DBUG_PRINT("info", ("row: %s, row_no: %ld", ds_row.str, row_no));
2043
dynstr_free(&ds_row);
2045
/* Remove any surrounding "'s from the query - if there is any */
2046
if (strip_surrounding(ds_query.str, '"', '"'))
2047
die("Mismatched \"'s around query '%s'", ds_query.str);
2050
if (mysql_real_query(mysql, ds_query.str, ds_query.length))
2051
die("Error running query '%s': %d %s", ds_query.str,
2052
mysql_errno(mysql), mysql_error(mysql));
2053
if (!(res= mysql_store_result(mysql)))
2054
die("Query '%s' didn't return a result set", ds_query.str);
2057
/* Find column number from the given column name */
2059
uint num_fields= mysql_num_fields(res);
2060
MYSQL_FIELD *fields= mysql_fetch_fields(res);
2062
for (i= 0; i < num_fields; i++)
2064
if (strcmp(fields[i].name, ds_col.str) == 0 &&
2065
strlen(fields[i].name) == ds_col.length)
2073
mysql_free_result(res);
2074
die("Could not find column '%s' in the result of '%s'",
2075
ds_col.str, ds_query.str);
2077
DBUG_PRINT("info", ("Found column %d with name '%s'",
2078
i, fields[i].name));
2080
dynstr_free(&ds_col);
2086
const char* value= "No such row";
2088
while ((row= mysql_fetch_row(res)))
2090
if (++rows == row_no)
2093
DBUG_PRINT("info", ("At row %ld, column %d is '%s'",
2094
row_no, col_no, row[col_no]));
2095
/* Found the row to get */
2104
eval_expr(var, value, 0);
2106
dynstr_free(&ds_query);
2107
mysql_free_result(res);
2113
void var_copy(VAR *dest, VAR *src)
2115
dest->int_val= src->int_val;
2116
dest->int_dirty= src->int_dirty;
2118
/* Alloc/realloc data for str_val in dest */
2119
if (dest->alloced_len < src->alloced_len &&
2120
!(dest->str_val= dest->str_val
2121
? my_realloc(dest->str_val, src->alloced_len, MYF(MY_WME))
2122
: my_malloc(src->alloced_len, MYF(MY_WME))))
2123
die("Out of memory");
2125
dest->alloced_len= src->alloced_len;
2127
/* Copy str_val data to dest */
2128
dest->str_val_len= src->str_val_len;
2129
if (src->str_val_len)
2130
memcpy(dest->str_val, src->str_val, src->str_val_len);
2134
void eval_expr(VAR *v, const char *p, const char **p_end)
2137
DBUG_ENTER("eval_expr");
2138
DBUG_PRINT("enter", ("p: '%s'", p));
2143
if ((vp= var_get(p, p_end, 0, 0)))
2150
var_query_set(v, p, p_end);
2155
/* Check if this is a "let $var= query_get_value()" */
2156
const char* get_value_str= "query_get_value";
2157
const size_t len= strlen(get_value_str);
2158
if (strncmp(p, get_value_str, len)==0)
2160
struct st_command command;
2161
memset(&command, 0, sizeof(command));
2162
command.query= (char*)p;
2163
command.first_word_len= len;
2164
command.first_argument= command.query + len;
2165
command.end= (char*)*p_end;
2166
var_set_query_get_value(&command, v);
2172
int new_val_len = (p_end && *p_end) ?
2173
(int) (*p_end - p) : (int) strlen(p);
2174
if (new_val_len + 1 >= v->alloced_len)
2176
static int MIN_VAR_ALLOC= 32;
2177
v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
2178
MIN_VAR_ALLOC : new_val_len + 1;
2180
v->str_val ? my_realloc(v->str_val, v->alloced_len+1,
2182
my_malloc(v->alloced_len+1, MYF(MY_WME))))
2183
die("Out of memory");
2185
v->str_val_len = new_val_len;
2186
memcpy(v->str_val, p, new_val_len);
2187
v->str_val[new_val_len] = 0;
2189
DBUG_PRINT("info", ("atoi on '%s', returns: %d", p, v->int_val));
2196
int open_file(const char *name)
2198
char buff[FN_REFLEN];
2199
DBUG_ENTER("open_file");
2200
DBUG_PRINT("enter", ("name: %s", name));
2201
if (!test_if_hard_path(name))
2203
strxmov(buff, opt_basedir, name, NullS);
2206
fn_format(buff, name, "", "", MY_UNPACK_FILENAME);
2208
if (cur_file == file_stack_end)
2209
die("Source directives are nesting too deep");
2211
if (!(cur_file->file = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0))))
2214
die("Could not open '%s' for reading", buff);
2216
cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
2223
Source and execute the given file
2227
query called command
2232
Open the file <file_name> and execute it
2236
void do_source(struct st_command *command)
2238
static DYNAMIC_STRING ds_filename;
2239
const struct command_arg source_args[] = {
2240
{ "filename", ARG_STRING, TRUE, &ds_filename, "File to source" }
2242
DBUG_ENTER("do_source");
2244
check_command_args(command, command->first_argument, source_args,
2245
sizeof(source_args)/sizeof(struct command_arg),
2249
If this file has already been sourced, don't source it again.
2250
It's already available in the q_lines cache.
2252
if (parser.current_line < (parser.read_lines - 1))
2256
DBUG_PRINT("info", ("sourcing file: %s", ds_filename.str));
2257
open_file(ds_filename.str);
2260
dynstr_free(&ds_filename);
2268
/* Variables used for temporary sh files used for emulating Unix on Windows */
2269
char tmp_sh_name[64], tmp_sh_cmd[70];
2272
void init_tmp_sh_file()
2275
/* Format a name for the tmp sh file that is unique for this process */
2276
my_snprintf(tmp_sh_name, sizeof(tmp_sh_name), "tmp_%d.sh", getpid());
2277
/* Format the command to execute in order to run the script */
2278
my_snprintf(tmp_sh_cmd, sizeof(tmp_sh_cmd), "sh %s", tmp_sh_name);
2283
void free_tmp_sh_file()
2286
my_delete(tmp_sh_name, MYF(0));
2292
FILE* my_popen(DYNAMIC_STRING *ds_cmd, const char *mode)
2294
#if defined __WIN__ && defined USE_CYGWIN
2295
/* Dump the command into a sh script file and execute with popen */
2296
str_to_file(tmp_sh_name, ds_cmd->str, ds_cmd->length);
2297
return popen(tmp_sh_cmd, mode);
2299
return popen(ds_cmd->str, mode);
2304
static void init_builtin_echo(void)
2309
/* Look for "echo.exe" in same dir as mysqltest was started from */
2310
dirname_part(builtin_echo, my_progname, &echo_length);
2311
fn_format(builtin_echo, ".\\echo.exe",
2312
builtin_echo, "", MYF(MY_REPLACE_DIR));
2314
/* Make sure echo.exe exists */
2315
if (access(builtin_echo, F_OK) != 0)
2333
ds_str The string to search and perform the replace in
2334
search_str The string to search for
2335
search_len Length of the string to search for
2336
replace_str The string to replace with
2337
replace_len Length of the string to replace with
2341
1 Could not find search_str in str
2344
static int replace(DYNAMIC_STRING *ds_str,
2345
const char *search_str, ulong search_len,
2346
const char *replace_str, ulong replace_len)
2348
DYNAMIC_STRING ds_tmp;
2349
const char *start= strstr(ds_str->str, search_str);
2352
init_dynamic_string(&ds_tmp, "",
2353
ds_str->length + replace_len, 256);
2354
dynstr_append_mem(&ds_tmp, ds_str->str, start - ds_str->str);
2355
dynstr_append_mem(&ds_tmp, replace_str, replace_len);
2356
dynstr_append(&ds_tmp, start + search_len);
2357
dynstr_set(ds_str, ds_tmp.str);
2358
dynstr_free(&ds_tmp);
2364
Execute given command.
2368
query called command
2373
Execute the text between exec and end of line in a subprocess.
2374
The error code returned from the subprocess is checked against the
2375
expected error array, previously set with the --error command.
2376
It can thus be used to execute a command that shall fail.
2379
Although mysqltest is executed from cygwin shell, the command will be
2380
executed in "cmd.exe". Thus commands like "rm" etc can NOT be used, use
2381
mysqltest commmand(s) like "remove_file" for that
2384
void do_exec(struct st_command *command)
2389
char *cmd= command->first_argument;
2390
DYNAMIC_STRING ds_cmd;
2391
DBUG_ENTER("do_exec");
2392
DBUG_PRINT("enter", ("cmd: '%s'", cmd));
2394
/* Skip leading space */
2395
while (*cmd && my_isspace(charset_info, *cmd))
2398
die("Missing argument in exec");
2399
command->last_argument= command->end;
2401
init_dynamic_string(&ds_cmd, 0, command->query_len+256, 256);
2402
/* Eval the command, thus replacing all environment variables */
2403
do_eval(&ds_cmd, cmd, command->end, !is_windows);
2405
/* Check if echo should be replaced with "builtin" echo */
2406
if (builtin_echo[0] && strncmp(cmd, "echo", 4) == 0)
2408
/* Replace echo with our "builtin" echo */
2409
replace(&ds_cmd, "echo", 4, builtin_echo, strlen(builtin_echo));
2414
/* Replace /dev/null with NUL */
2415
while(replace(&ds_cmd, "/dev/null", 9, "NUL", 3) == 0)
2417
/* Replace "closed stdout" with non existing output fd */
2418
while(replace(&ds_cmd, ">&-", 3, ">&4", 3) == 0)
2423
DBUG_PRINT("info", ("Executing '%s' as '%s'",
2424
command->first_argument, ds_cmd.str));
2426
if (!(res_file= my_popen(&ds_cmd, "r")) && command->abort_on_error)
2428
dynstr_free(&ds_cmd);
2429
die("popen(\"%s\", \"r\") failed", command->first_argument);
2432
while (fgets(buf, sizeof(buf), res_file))
2434
if (disable_result_log)
2436
buf[strlen(buf)-1]=0;
2437
DBUG_PRINT("exec_result",("%s", buf));
2441
replace_dynstr_append(&ds_res, buf);
2444
error= pclose(res_file);
2447
uint status= WEXITSTATUS(error), i;
2450
if (command->abort_on_error)
2452
log_msg("exec of '%s' failed, error: %d, status: %d, errno: %d",
2453
ds_cmd.str, error, status, errno);
2454
dynstr_free(&ds_cmd);
2455
die("command \"%s\" failed", command->first_argument);
2459
("error: %d, status: %d", error, status));
2460
for (i= 0; i < command->expected_errors.count; i++)
2462
DBUG_PRINT("info", ("expected error: %d",
2463
command->expected_errors.err[i].code.errnum));
2464
if ((command->expected_errors.err[i].type == ERR_ERRNO) &&
2465
(command->expected_errors.err[i].code.errnum == status))
2468
DBUG_PRINT("info", ("command \"%s\" failed with expected error: %d",
2469
command->first_argument, status));
2474
dynstr_free(&ds_cmd);
2475
die("command \"%s\" failed with wrong error: %d",
2476
command->first_argument, status);
2479
else if (command->expected_errors.err[0].type == ERR_ERRNO &&
2480
command->expected_errors.err[0].code.errnum != 0)
2482
/* Error code we wanted was != 0, i.e. not an expected success */
2483
log_msg("exec of '%s failed, error: %d, errno: %d",
2484
ds_cmd.str, error, errno);
2485
dynstr_free(&ds_cmd);
2486
die("command \"%s\" succeeded - should have failed with errno %d...",
2487
command->first_argument, command->expected_errors.err[0].code.errnum);
2490
dynstr_free(&ds_cmd);
2502
Decrease or increase the value of a variable
2506
query called command
2507
operator operation to perform on the var
2515
int do_modify_var(struct st_command *command,
2516
enum enum_operator operator)
2518
const char *p= command->first_argument;
2521
die("Missing argument to %.*s", command->first_word_len, command->query);
2523
die("The argument to %.*s must be a variable (start with $)",
2524
command->first_word_len, command->query);
2525
v= var_get(p, &p, 1, 0);
2534
die("Invalid operator to do_modify_var");
2538
command->last_argument= (char*)++p;
2544
Wrapper for 'system' function
2547
If mysqltest is executed from cygwin shell, the command will be
2548
executed in the "windows command interpreter" cmd.exe and we prepend "sh"
2549
to make it be executed by cygwins "bash". Thus commands like "rm",
2550
"mkdir" as well as shellscripts can executed by "system" in Windows.
2554
int my_system(DYNAMIC_STRING* ds_cmd)
2556
#if defined __WIN__ && defined USE_CYGWIN
2557
/* Dump the command into a sh script file and execute with system */
2558
str_to_file(tmp_sh_name, ds_cmd->str, ds_cmd->length);
2559
return system(tmp_sh_cmd);
2561
return system(ds_cmd->str);
2569
command called command
2574
Eval the query to expand any $variables in the command.
2575
Execute the command with the "system" command.
2579
void do_system(struct st_command *command)
2581
DYNAMIC_STRING ds_cmd;
2582
DBUG_ENTER("do_system");
2584
if (strlen(command->first_argument) == 0)
2585
die("Missing arguments to system, nothing to do!");
2587
init_dynamic_string(&ds_cmd, 0, command->query_len + 64, 256);
2589
/* Eval the system command, thus replacing all environment variables */
2590
do_eval(&ds_cmd, command->first_argument, command->end, !is_windows);
2594
/* Replace /dev/null with NUL */
2595
while(replace(&ds_cmd, "/dev/null", 9, "NUL", 3) == 0)
2601
DBUG_PRINT("info", ("running system command '%s' as '%s'",
2602
command->first_argument, ds_cmd.str));
2603
if (my_system(&ds_cmd))
2605
if (command->abort_on_error)
2606
die("system command '%s' failed", command->first_argument);
2608
/* If ! abort_on_error, log message and continue */
2609
dynstr_append(&ds_res, "system command '");
2610
replace_dynstr_append(&ds_res, command->first_argument);
2611
dynstr_append(&ds_res, "' failed\n");
2614
command->last_argument= command->end;
2615
dynstr_free(&ds_cmd);
2623
command called command
2626
remove_file <file_name>
2627
Remove the file <file_name>
2630
void do_remove_file(struct st_command *command)
2633
static DYNAMIC_STRING ds_filename;
2634
const struct command_arg rm_args[] = {
2635
{ "filename", ARG_STRING, TRUE, &ds_filename, "File to delete" }
2637
DBUG_ENTER("do_remove_file");
2639
check_command_args(command, command->first_argument,
2640
rm_args, sizeof(rm_args)/sizeof(struct command_arg),
2643
DBUG_PRINT("info", ("removing file: %s", ds_filename.str));
2644
error= my_delete(ds_filename.str, MYF(0)) != 0;
2645
handle_command_error(command, error);
2646
dynstr_free(&ds_filename);
2654
command command handle
2657
copy_file <from_file> <to_file>
2658
Copy <from_file> to <to_file>
2660
NOTE! Will fail if <to_file> exists
2663
void do_copy_file(struct st_command *command)
2666
static DYNAMIC_STRING ds_from_file;
2667
static DYNAMIC_STRING ds_to_file;
2668
const struct command_arg copy_file_args[] = {
2669
{ "from_file", ARG_STRING, TRUE, &ds_from_file, "Filename to copy from" },
2670
{ "to_file", ARG_STRING, TRUE, &ds_to_file, "Filename to copy to" }
2672
DBUG_ENTER("do_copy_file");
2674
check_command_args(command, command->first_argument,
2676
sizeof(copy_file_args)/sizeof(struct command_arg),
2679
DBUG_PRINT("info", ("Copy %s to %s", ds_from_file.str, ds_to_file.str));
2680
error= (my_copy(ds_from_file.str, ds_to_file.str,
2681
MYF(MY_DONT_OVERWRITE_FILE)) != 0);
2682
handle_command_error(command, error);
2683
dynstr_free(&ds_from_file);
2684
dynstr_free(&ds_to_file);
2692
command command handle
2695
chmod <octal> <file_name>
2696
Change file permission of <file_name>
2700
void do_chmod_file(struct st_command *command)
2703
static DYNAMIC_STRING ds_mode;
2704
static DYNAMIC_STRING ds_file;
2705
const struct command_arg chmod_file_args[] = {
2706
{ "mode", ARG_STRING, TRUE, &ds_mode, "Mode of file(octal) ex. 0660"},
2707
{ "filename", ARG_STRING, TRUE, &ds_file, "Filename of file to modify" }
2709
DBUG_ENTER("do_chmod_file");
2711
check_command_args(command, command->first_argument,
2713
sizeof(chmod_file_args)/sizeof(struct command_arg),
2716
/* Parse what mode to set */
2717
if (ds_mode.length != 4 ||
2718
str2int(ds_mode.str, 8, 0, INT_MAX, &mode) == NullS)
2719
die("You must write a 4 digit octal number for mode");
2721
DBUG_PRINT("info", ("chmod %o %s", (uint)mode, ds_file.str));
2722
handle_command_error(command, chmod(ds_file.str, mode));
2723
dynstr_free(&ds_mode);
2724
dynstr_free(&ds_file);
2732
command called command
2735
fiile_exist <file_name>
2736
Check if file <file_name> exists
2739
void do_file_exist(struct st_command *command)
2742
static DYNAMIC_STRING ds_filename;
2743
const struct command_arg file_exist_args[] = {
2744
{ "filename", ARG_STRING, TRUE, &ds_filename, "File to check if it exist" }
2746
DBUG_ENTER("do_file_exist");
2748
check_command_args(command, command->first_argument,
2750
sizeof(file_exist_args)/sizeof(struct command_arg),
2753
DBUG_PRINT("info", ("Checking for existence of file: %s", ds_filename.str));
2754
error= (access(ds_filename.str, F_OK) != 0);
2755
handle_command_error(command, error);
2756
dynstr_free(&ds_filename);
2764
command called command
2768
Create the directory <dir_name>
2771
void do_mkdir(struct st_command *command)
2774
static DYNAMIC_STRING ds_dirname;
2775
const struct command_arg mkdir_args[] = {
2776
"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to create"
2778
DBUG_ENTER("do_mkdir");
2780
check_command_args(command, command->first_argument,
2781
mkdir_args, sizeof(mkdir_args)/sizeof(struct command_arg),
2784
DBUG_PRINT("info", ("creating directory: %s", ds_dirname.str));
2785
error= my_mkdir(ds_dirname.str, 0777, MYF(0)) != 0;
2786
handle_command_error(command, error);
2787
dynstr_free(&ds_dirname);
2794
command called command
2798
Remove the empty directory <dir_name>
2801
void do_rmdir(struct st_command *command)
2804
static DYNAMIC_STRING ds_dirname;
2805
const struct command_arg rmdir_args[] = {
2806
"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to remove"
2808
DBUG_ENTER("do_rmdir");
2810
check_command_args(command, command->first_argument,
2811
rmdir_args, sizeof(rmdir_args)/sizeof(struct command_arg),
2814
DBUG_PRINT("info", ("removing directory: %s", ds_dirname.str));
2815
error= rmdir(ds_dirname.str) != 0;
2816
handle_command_error(command, error);
2817
dynstr_free(&ds_dirname);
2823
Read characters from line buffer or file. This is needed to allow
2824
my_ungetc() to buffer MAX_DELIMITER_LENGTH characters for a file
2827
This works as long as one doesn't change files (with 'source file_name')
2828
when there is things pushed into the buffer. This should however not
2829
happen for any tests in the test suite.
2832
int my_getc(FILE *file)
2834
if (line_buffer_pos == line_buffer)
2836
return *--line_buffer_pos;
2840
void my_ungetc(int c)
2842
*line_buffer_pos++= (char) c;
2846
void read_until_delimiter(DYNAMIC_STRING *ds,
2847
DYNAMIC_STRING *ds_delimiter)
2850
DBUG_ENTER("read_until_delimiter");
2851
DBUG_PRINT("enter", ("delimiter: %s, length: %u",
2852
ds_delimiter->str, (uint) ds_delimiter->length));
2854
if (ds_delimiter->length > MAX_DELIMITER_LENGTH)
2855
die("Max delimiter length(%d) exceeded", MAX_DELIMITER_LENGTH);
2857
/* Read from file until delimiter is found */
2860
c= my_getc(cur_file->file);
2866
/* Skip newline from the same line as the command */
2867
if (start_lineno == (cur_file->lineno - 1))
2870
else if (start_lineno == cur_file->lineno)
2873
No characters except \n are allowed on
2874
the same line as the command
2876
die("Trailing characters found after command");
2879
if (feof(cur_file->file))
2880
die("End of file encountered before '%s' delimiter was found",
2883
if (match_delimiter(c, ds_delimiter->str, ds_delimiter->length))
2885
DBUG_PRINT("exit", ("Found delimiter '%s'", ds_delimiter->str));
2888
dynstr_append_mem(ds, (const char*)&c, 1);
2890
DBUG_PRINT("exit", ("ds: %s", ds->str));
2895
void do_write_file_command(struct st_command *command, my_bool append)
2897
static DYNAMIC_STRING ds_content;
2898
static DYNAMIC_STRING ds_filename;
2899
static DYNAMIC_STRING ds_delimiter;
2900
const struct command_arg write_file_args[] = {
2901
{ "filename", ARG_STRING, TRUE, &ds_filename, "File to write to" },
2902
{ "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" }
2904
DBUG_ENTER("do_write_file");
2906
check_command_args(command,
2907
command->first_argument,
2909
sizeof(write_file_args)/sizeof(struct command_arg),
2912
/* If no delimiter was provided, use EOF */
2913
if (ds_delimiter.length == 0)
2914
dynstr_set(&ds_delimiter, "EOF");
2916
if (!append && access(ds_filename.str, F_OK) == 0)
2918
/* The file should not be overwritten */
2919
die("File already exist: '%s'", ds_filename.str);
2922
init_dynamic_string(&ds_content, "", 1024, 1024);
2923
read_until_delimiter(&ds_content, &ds_delimiter);
2924
DBUG_PRINT("info", ("Writing to file: %s", ds_filename.str));
2925
str_to_file2(ds_filename.str, ds_content.str, ds_content.length, append);
2926
dynstr_free(&ds_content);
2927
dynstr_free(&ds_filename);
2928
dynstr_free(&ds_delimiter);
2936
command called command
2939
write_file <file_name> [<delimiter>];
2940
<what to write line 1>
2942
< what to write line n>
2945
--write_file <file_name>;
2946
<what to write line 1>
2948
< what to write line n>
2951
Write everything between the "write_file" command and 'delimiter'
2954
NOTE! Will fail if <file_name> exists
2956
Default <delimiter> is EOF
2960
void do_write_file(struct st_command *command)
2962
do_write_file_command(command, FALSE);
2969
command called command
2972
append_file <file_name> [<delimiter>];
2973
<what to write line 1>
2975
< what to write line n>
2978
--append_file <file_name>;
2979
<what to write line 1>
2981
< what to write line n>
2984
Append everything between the "append_file" command
2985
and 'delimiter' to "file_name"
2987
Default <delimiter> is EOF
2991
void do_append_file(struct st_command *command)
2993
do_write_file_command(command, TRUE);
3000
command called command
3003
cat_file <file_name>;
3005
Print the given file to result log
3009
void do_cat_file(struct st_command *command)
3011
static DYNAMIC_STRING ds_filename;
3012
const struct command_arg cat_file_args[] = {
3013
{ "filename", ARG_STRING, TRUE, &ds_filename, "File to read from" }
3015
DBUG_ENTER("do_cat_file");
3017
check_command_args(command,
3018
command->first_argument,
3020
sizeof(cat_file_args)/sizeof(struct command_arg),
3023
DBUG_PRINT("info", ("Reading from, file: %s", ds_filename.str));
3025
cat_file(&ds_res, ds_filename.str);
3027
dynstr_free(&ds_filename);
3035
command called command
3038
diff_files <file1> <file2>;
3040
Fails if the two files differ.
3044
void do_diff_files(struct st_command *command)
3047
static DYNAMIC_STRING ds_filename;
3048
static DYNAMIC_STRING ds_filename2;
3049
const struct command_arg diff_file_args[] = {
3050
{ "file1", ARG_STRING, TRUE, &ds_filename, "First file to diff" },
3051
{ "file2", ARG_STRING, TRUE, &ds_filename2, "Second file to diff" }
3053
DBUG_ENTER("do_diff_files");
3055
check_command_args(command,
3056
command->first_argument,
3058
sizeof(diff_file_args)/sizeof(struct command_arg),
3061
if ((error= compare_files(ds_filename.str, ds_filename2.str)))
3063
/* Compare of the two files failed, append them to output
3064
so the failure can be analyzed
3066
show_diff(&ds_res, ds_filename.str, ds_filename2.str);
3069
dynstr_free(&ds_filename);
3070
dynstr_free(&ds_filename2);
3071
handle_command_error(command, error);
3076
struct st_connection * find_connection_by_name(const char *name)
3078
struct st_connection *con;
3079
for (con= connections; con < next_con; con++)
3081
if (!strcmp(con->name, name))
3086
return 0; /* Connection not found */
3093
command called command
3096
Sends a simple quit command to the server for the named connection.
3100
void do_send_quit(struct st_command *command)
3102
char *p= command->first_argument, *name;
3103
struct st_connection *con;
3105
DBUG_ENTER("do_send_quit");
3106
DBUG_PRINT("enter",("name: '%s'",p));
3109
die("Missing connection name in send_quit");
3111
while (*p && !my_isspace(charset_info,*p))
3116
command->last_argument= p;
3118
if (!(con= find_connection_by_name(name)))
3119
die("connection '%s' not found in connection pool", name);
3121
simple_command(&con->mysql,COM_QUIT,0,0,1);
3130
command called command
3133
change_user [<user>], [<passwd>], [<db>]
3134
<user> - user to change to
3135
<passwd> - user password
3136
<db> - default database
3138
Changes the user and causes the database specified by db to become
3139
the default (current) database for the the current connection.
3143
void do_change_user(struct st_command *command)
3145
MYSQL *mysql = &cur_con->mysql;
3146
/* static keyword to make the NetWare compiler happy. */
3147
static DYNAMIC_STRING ds_user, ds_passwd, ds_db;
3148
const struct command_arg change_user_args[] = {
3149
{ "user", ARG_STRING, FALSE, &ds_user, "User to connect as" },
3150
{ "password", ARG_STRING, FALSE, &ds_passwd, "Password used when connecting" },
3151
{ "database", ARG_STRING, FALSE, &ds_db, "Database to select after connect" },
3154
DBUG_ENTER("do_change_user");
3156
check_command_args(command, command->first_argument,
3158
sizeof(change_user_args)/sizeof(struct command_arg),
3163
mysql_stmt_close(cur_con->stmt);
3164
cur_con->stmt= NULL;
3167
if (!ds_user.length)
3168
dynstr_set(&ds_user, mysql->user);
3170
if (!ds_passwd.length)
3171
dynstr_set(&ds_passwd, mysql->passwd);
3174
dynstr_set(&ds_db, mysql->db);
3176
DBUG_PRINT("info",("connection: '%s' user: '%s' password: '%s' database: '%s'",
3177
cur_con->name, ds_user.str, ds_passwd.str, ds_db.str));
3179
if (mysql_change_user(mysql, ds_user.str, ds_passwd.str, ds_db.str))
3180
die("change user failed: %s", mysql_error(mysql));
3182
dynstr_free(&ds_user);
3183
dynstr_free(&ds_passwd);
3184
dynstr_free(&ds_db);
3193
command command handle
3202
Execute everything after "perl" until <delimiter> as perl.
3203
Useful for doing more advanced things
3204
but still being able to execute it on all platforms.
3206
Default <delimiter> is EOF
3209
void do_perl(struct st_command *command)
3214
char buf[FN_REFLEN];
3215
char temp_file_path[FN_REFLEN];
3216
static DYNAMIC_STRING ds_script;
3217
static DYNAMIC_STRING ds_delimiter;
3218
const struct command_arg perl_args[] = {
3219
{ "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" }
3221
DBUG_ENTER("do_perl");
3223
check_command_args(command,
3224
command->first_argument,
3226
sizeof(perl_args)/sizeof(struct command_arg),
3229
/* If no delimiter was provided, use EOF */
3230
if (ds_delimiter.length == 0)
3231
dynstr_set(&ds_delimiter, "EOF");
3233
init_dynamic_string(&ds_script, "", 1024, 1024);
3234
read_until_delimiter(&ds_script, &ds_delimiter);
3236
DBUG_PRINT("info", ("Executing perl: %s", ds_script.str));
3238
/* Create temporary file name */
3239
if ((fd= create_temp_file(temp_file_path, getenv("MYSQLTEST_VARDIR"),
3240
"tmp", O_CREAT | O_SHARE | O_RDWR,
3242
die("Failed to create temporary file for perl command");
3243
my_close(fd, MYF(0));
3245
str_to_file(temp_file_path, ds_script.str, ds_script.length);
3247
/* Format the "perl <filename>" command */
3248
my_snprintf(buf, sizeof(buf), "perl %s", temp_file_path);
3250
if (!(res_file= popen(buf, "r")) && command->abort_on_error)
3251
die("popen(\"%s\", \"r\") failed", buf);
3253
while (fgets(buf, sizeof(buf), res_file))
3255
if (disable_result_log)
3257
buf[strlen(buf)-1]=0;
3258
DBUG_PRINT("exec_result",("%s", buf));
3262
replace_dynstr_append(&ds_res, buf);
3265
error= pclose(res_file);
3267
/* Remove the temporary file */
3268
my_delete(temp_file_path, MYF(0));
3270
handle_command_error(command, WEXITSTATUS(error));
3271
dynstr_free(&ds_script);
3272
dynstr_free(&ds_delimiter);
3278
Print the content between echo and <delimiter> to result file.
3279
Evaluate all variables in the string before printing, allow
3280
for variable names to be escaped using \
3284
command called command
3288
Print the text after echo until end of command to result file
3291
Print the content of the variable <var_name> to result file
3293
echo Some text $<var_name>
3294
Print "Some text" plus the content of the variable <var_name> to
3297
echo Some text \$<var_name>
3298
Print "Some text" plus $<var_name> to result file
3301
int do_echo(struct st_command *command)
3303
DYNAMIC_STRING ds_echo;
3304
DBUG_ENTER("do_echo");
3306
init_dynamic_string(&ds_echo, "", command->query_len, 256);
3307
do_eval(&ds_echo, command->first_argument, command->end, FALSE);
3308
dynstr_append_mem(&ds_res, ds_echo.str, ds_echo.length);
3309
dynstr_append_mem(&ds_res, "\n", 1);
3310
dynstr_free(&ds_echo);
3311
command->last_argument= command->end;
3316
void do_wait_for_slave_to_stop(struct st_command *c __attribute__((unused)))
3318
static int SLAVE_POLL_INTERVAL= 300000;
3319
MYSQL* mysql = &cur_con->mysql;
3322
MYSQL_RES *res= NULL;
3326
if (mysql_query(mysql,"show status like 'Slave_running'") ||
3327
!(res=mysql_store_result(mysql)))
3328
die("Query failed while probing slave for stop: %s",
3329
mysql_error(mysql));
3330
if (!(row=mysql_fetch_row(res)) || !row[1])
3332
mysql_free_result(res);
3333
die("Strange result from query while probing slave for stop");
3335
done = !strcmp(row[1],"OFF");
3336
mysql_free_result(res);
3339
my_sleep(SLAVE_POLL_INTERVAL);
3345
void do_sync_with_master2(long offset)
3349
MYSQL *mysql= &cur_con->mysql;
3350
char query_buf[FN_REFLEN+128];
3353
if (!master_pos.file[0])
3354
die("Calling 'sync_with_master' without calling 'save_master_pos'");
3356
sprintf(query_buf, "select master_pos_wait('%s', %ld)", master_pos.file,
3357
master_pos.pos + offset);
3361
if (mysql_query(mysql, query_buf))
3362
die("failed in '%s': %d: %s", query_buf, mysql_errno(mysql),
3363
mysql_error(mysql));
3365
if (!(res= mysql_store_result(mysql)))
3366
die("mysql_store_result() returned NULL for '%s'", query_buf);
3367
if (!(row= mysql_fetch_row(res)))
3369
mysql_free_result(res);
3370
die("empty result in %s", query_buf);
3375
It may be that the slave SQL thread has not started yet, though START
3376
SLAVE has been issued ?
3378
mysql_free_result(res);
3381
show_query(mysql, "SHOW MASTER STATUS");
3382
show_query(mysql, "SHOW SLAVE STATUS");
3383
die("could not sync with master ('%s' returned NULL)", query_buf);
3385
sleep(1); /* So at most we will wait 30 seconds and make 31 tries */
3386
goto wait_for_position;
3388
mysql_free_result(res);
3393
void do_sync_with_master(struct st_command *command)
3396
char *p= command->first_argument;
3397
const char *offset_start= p;
3400
for (; my_isdigit(charset_info, *p); p++)
3401
offset = offset * 10 + *p - '0';
3403
if(*p && !my_isspace(charset_info, *p))
3404
die("Invalid integer argument \"%s\"", offset_start);
3405
command->last_argument= p;
3407
do_sync_with_master2(offset);
3413
when ndb binlog is on, this call will wait until last updated epoch
3414
(locally in the mysqld) has been received into the binlog
3416
int do_save_master_pos()
3420
MYSQL *mysql = &cur_con->mysql;
3422
DBUG_ENTER("do_save_master_pos");
3424
#ifdef HAVE_NDB_BINLOG
3426
Wait for ndb binlog to be up-to-date with all changes
3427
done on the local mysql server
3430
ulong have_ndbcluster;
3431
if (mysql_query(mysql, query= "show variables like 'have_ndbcluster'"))
3432
die("'%s' failed: %d %s", query,
3433
mysql_errno(mysql), mysql_error(mysql));
3434
if (!(res= mysql_store_result(mysql)))
3435
die("mysql_store_result() returned NULL for '%s'", query);
3436
if (!(row= mysql_fetch_row(res)))
3437
die("Query '%s' returned empty result", query);
3439
have_ndbcluster= strcmp("YES", row[1]) == 0;
3440
mysql_free_result(res);
3442
if (have_ndbcluster)
3444
ulonglong start_epoch= 0, handled_epoch= 0,
3445
latest_epoch=0, latest_trans_epoch=0,
3446
latest_handled_binlog_epoch= 0, latest_received_binlog_epoch= 0,
3447
latest_applied_binlog_epoch= 0;
3452
const char binlog[]= "binlog";
3453
const char latest_epoch_str[]=
3455
const char latest_trans_epoch_str[]=
3456
"latest_trans_epoch=";
3457
const char latest_received_binlog_epoch_str[]=
3458
"latest_received_binlog_epoch";
3459
const char latest_handled_binlog_epoch_str[]=
3460
"latest_handled_binlog_epoch=";
3461
const char latest_applied_binlog_epoch_str[]=
3462
"latest_applied_binlog_epoch=";
3465
if (mysql_query(mysql, query= "show engine ndb status"))
3466
die("failed in '%s': %d %s", query,
3467
mysql_errno(mysql), mysql_error(mysql));
3468
if (!(res= mysql_store_result(mysql)))
3469
die("mysql_store_result() returned NULL for '%s'", query);
3470
while ((row= mysql_fetch_row(res)))
3472
if (strcmp(row[1], binlog) == 0)
3474
const char *status= row[2];
3477
while (*status && strncmp(status, latest_epoch_str,
3478
sizeof(latest_epoch_str)-1))
3482
status+= sizeof(latest_epoch_str)-1;
3483
latest_epoch= strtoull(status, (char**) 0, 10);
3486
die("result does not contain '%s' in '%s'",
3487
latest_epoch_str, query);
3488
/* latest_trans_epoch */
3489
while (*status && strncmp(status, latest_trans_epoch_str,
3490
sizeof(latest_trans_epoch_str)-1))
3494
status+= sizeof(latest_trans_epoch_str)-1;
3495
latest_trans_epoch= strtoull(status, (char**) 0, 10);
3498
die("result does not contain '%s' in '%s'",
3499
latest_trans_epoch_str, query);
3500
/* latest_received_binlog_epoch */
3502
strncmp(status, latest_received_binlog_epoch_str,
3503
sizeof(latest_received_binlog_epoch_str)-1))
3507
status+= sizeof(latest_received_binlog_epoch_str)-1;
3508
latest_received_binlog_epoch= strtoull(status, (char**) 0, 10);
3511
die("result does not contain '%s' in '%s'",
3512
latest_received_binlog_epoch_str, query);
3513
/* latest_handled_binlog */
3515
strncmp(status, latest_handled_binlog_epoch_str,
3516
sizeof(latest_handled_binlog_epoch_str)-1))
3520
status+= sizeof(latest_handled_binlog_epoch_str)-1;
3521
latest_handled_binlog_epoch= strtoull(status, (char**) 0, 10);
3524
die("result does not contain '%s' in '%s'",
3525
latest_handled_binlog_epoch_str, query);
3526
/* latest_applied_binlog_epoch */
3528
strncmp(status, latest_applied_binlog_epoch_str,
3529
sizeof(latest_applied_binlog_epoch_str)-1))
3533
status+= sizeof(latest_applied_binlog_epoch_str)-1;
3534
latest_applied_binlog_epoch= strtoull(status, (char**) 0, 10);
3537
die("result does not contain '%s' in '%s'",
3538
latest_applied_binlog_epoch_str, query);
3540
start_epoch= latest_trans_epoch;
3545
die("result does not contain '%s' in '%s'",
3547
if (latest_handled_binlog_epoch > handled_epoch)
3549
handled_epoch= latest_handled_binlog_epoch;
3551
if (latest_handled_binlog_epoch >= start_epoch)
3553
else if (count > 30)
3557
mysql_free_result(res);
3562
if (mysql_query(mysql, query= "show master status"))
3563
die("failed in 'show master status': %d %s",
3564
mysql_errno(mysql), mysql_error(mysql));
3566
if (!(res = mysql_store_result(mysql)))
3567
die("mysql_store_result() retuned NULL for '%s'", query);
3568
if (!(row = mysql_fetch_row(res)))
3569
die("empty result in show master status");
3570
strnmov(master_pos.file, row[0], sizeof(master_pos.file)-1);
3571
master_pos.pos = strtoul(row[1], (char**) 0, 10);
3572
mysql_free_result(res);
3578
Assign the variable <var_name> with <var_val>
3582
query called command
3585
let $<var_name>=<var_val><delimiter>
3587
<var_name> - is the string string found between the $ and =
3588
<var_val> - is the content between the = and <delimiter>, it may span
3589
multiple line and contain any characters except <delimiter>
3590
<delimiter> - is a string containing of one or more chars, default is ;
3593
Program will die if error detected
3596
void do_let(struct st_command *command)
3598
char *p= command->first_argument;
3599
char *var_name, *var_name_end;
3600
DYNAMIC_STRING let_rhs_expr;
3601
DBUG_ENTER("do_let");
3603
init_dynamic_string(&let_rhs_expr, "", 512, 2048);
3605
/* Find <var_name> */
3607
die("Missing arguments to let");
3609
while (*p && (*p != '=') && !my_isspace(charset_info,*p))
3612
if (var_name == var_name_end ||
3613
(var_name+1 == var_name_end && *var_name == '$'))
3614
die("Missing variable name in let");
3615
while (my_isspace(charset_info,*p))
3618
die("Missing assignment operator in let");
3620
/* Find start of <var_val> */
3621
while (*p && my_isspace(charset_info,*p))
3624
do_eval(&let_rhs_expr, p, command->end, FALSE);
3626
command->last_argument= command->end;
3627
/* Assign var_val to var_name */
3628
var_set(var_name, var_name_end, let_rhs_expr.str,
3629
(let_rhs_expr.str + let_rhs_expr.length));
3630
dynstr_free(&let_rhs_expr);
3636
Sleep the number of specified seconds
3641
real_sleep use the value from opt_sleep as number of seconds to sleep
3642
if real_sleep is false
3646
real_sleep <seconds>
3648
The difference between the sleep and real_sleep commands is that sleep
3649
uses the delay from the --sleep command-line option if there is one.
3650
(If the --sleep option is not given, the sleep command uses the delay
3651
specified by its argument.) The real_sleep command always uses the
3652
delay specified by its argument. The logic is that sometimes delays are
3653
cpu-dependent, and --sleep can be used to set this delay. real_sleep is
3654
used for cpu-independent delays.
3657
int do_sleep(struct st_command *command, my_bool real_sleep)
3660
char *p= command->first_argument;
3661
char *sleep_start, *sleep_end= command->end;
3664
while (my_isspace(charset_info, *p))
3667
die("Missing argument to %.*s", command->first_word_len, command->query);
3669
/* Check that arg starts with a digit, not handled by my_strtod */
3670
if (!my_isdigit(charset_info, *sleep_start))
3671
die("Invalid argument to %.*s \"%s\"", command->first_word_len,
3672
command->query,command->first_argument);
3673
sleep_val= my_strtod(sleep_start, &sleep_end, &error);
3675
die("Invalid argument to %.*s \"%s\"", command->first_word_len,
3676
command->query, command->first_argument);
3678
/* Fixed sleep time selected by --sleep option */
3679
if (opt_sleep >= 0 && !real_sleep)
3680
sleep_val= opt_sleep;
3682
DBUG_PRINT("info", ("sleep_val: %f", sleep_val));
3684
my_sleep((ulong) (sleep_val * 1000000L));
3685
command->last_argument= sleep_end;
3690
void do_get_file_name(struct st_command *command,
3691
char* dest, uint dest_max_len)
3693
char *p= command->first_argument, *name;
3695
die("Missing file name argument");
3697
while (*p && !my_isspace(charset_info,*p))
3701
command->last_argument= p;
3702
strmake(dest, name, dest_max_len - 1);
3706
void do_set_charset(struct st_command *command)
3708
char *charset_name= command->first_argument;
3711
if (!charset_name || !*charset_name)
3712
die("Missing charset name in 'character_set'");
3713
/* Remove end space */
3715
while (*p && !my_isspace(charset_info,*p))
3719
command->last_argument= p;
3720
charset_info= get_charset_by_csname(charset_name,MY_CS_PRIMARY,MYF(MY_WME));
3722
abort_not_supported_test("Test requires charset '%s'", charset_name);
3726
#if MYSQL_VERSION_ID >= 50000
3727
/* List of error names to error codes, available from 5.0 */
3734
static st_error global_error_names[] =
3736
#include <mysqld_ername.h>
3740
uint get_errcode_from_name(char *error_name, char *error_end)
3742
/* SQL error as string */
3743
st_error *e= global_error_names;
3745
DBUG_ENTER("get_errcode_from_name");
3746
DBUG_PRINT("enter", ("error_name: %s", error_name));
3748
/* Loop through the array of known error names */
3749
for (; e->name; e++)
3752
If we get a match, we need to check the length of the name we
3753
matched against in case it was longer than what we are checking
3754
(as in ER_WRONG_VALUE vs. ER_WRONG_VALUE_COUNT).
3756
if (!strncmp(error_name, e->name, (int) (error_end - error_name)) &&
3757
(uint) strlen(e->name) == (uint) (error_end - error_name))
3759
DBUG_RETURN(e->code);
3763
die("Unknown SQL error name '%s'", error_name);
3767
uint get_errcode_from_name(char *error_name __attribute__((unused)),
3768
char *error_end __attribute__((unused)))
3770
abort_not_in_this_version();
3771
return 0; /* Never reached */
3777
void do_get_errcodes(struct st_command *command)
3779
struct st_match_err *to= saved_expected_errors.err;
3780
char *p= command->first_argument;
3783
DBUG_ENTER("do_get_errcodes");
3786
die("Missing argument(s) to 'error'");
3792
/* Skip leading spaces */
3793
while (*p && *p == ' ')
3798
while (*end && *end != ',' && *end != ' ')
3803
char *to_ptr= to->code.sqlstate;
3807
- Must be SQLSTATE_LENGTH long
3808
- May contain only digits[0-9] and _uppercase_ letters
3810
p++; /* Step past the S */
3811
if ((end - p) != SQLSTATE_LENGTH)
3812
die("The sqlstate must be exactly %d chars long", SQLSTATE_LENGTH);
3814
/* Check sqlstate string validity */
3815
while (*p && p < end)
3817
if (my_isdigit(charset_info, *p) || my_isupper(charset_info, *p))
3820
die("The sqlstate may only consist of digits[0-9] " \
3821
"and _uppercase_ letters");
3825
to->type= ERR_SQLSTATE;
3826
DBUG_PRINT("info", ("ERR_SQLSTATE: %s", to->code.sqlstate));
3830
die("The sqlstate definition must start with an uppercase S");
3834
/* Error name string */
3836
DBUG_PRINT("info", ("Error name: %s", p));
3837
to->code.errnum= get_errcode_from_name(p, end);
3838
to->type= ERR_ERRNO;
3839
DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum));
3843
die("The error name definition must start with an uppercase E");
3849
/* Check that the string passed to str2int only contain digits */
3850
while (*p && p != end)
3852
if (!my_isdigit(charset_info, *p))
3853
die("Invalid argument to error: '%s' - "\
3854
"the errno may only consist of digits[0-9]",
3855
command->first_argument);
3859
/* Convert the sting to int */
3860
if (!str2int(start, 10, (long) INT_MIN, (long) INT_MAX, &val))
3861
die("Invalid argument to error: '%s'", command->first_argument);
3863
to->code.errnum= (uint) val;
3864
to->type= ERR_ERRNO;
3865
DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum));
3870
if (count >= (sizeof(saved_expected_errors.err) /
3871
sizeof(struct st_match_err)))
3872
die("Too many errorcodes specified");
3874
/* Set pointer to the end of the last error code */
3878
while (*p && *p != ',')
3882
p++; /* Step past ',' */
3886
command->last_argument= p;
3887
to->type= ERR_EMPTY; /* End of data */
3889
DBUG_PRINT("info", ("Expected errors: %d", count));
3890
saved_expected_errors.count= count;
3896
Get a string; Return ptr to end of string
3897
Strings may be surrounded by " or '
3899
If string is a '$variable', return the value of the variable.
3902
char *get_string(char **to_ptr, char **from_ptr,
3903
struct st_command *command)
3906
char *to= *to_ptr, *from= *from_ptr, *start=to;
3907
DBUG_ENTER("get_string");
3909
/* Find separator */
3910
if (*from == '"' || *from == '\'')
3913
sep=' '; /* Separated with space */
3915
for ( ; (c=*from) ; from++)
3917
if (c == '\\' && from[1])
3918
{ /* Escaped character */
3919
/* We can't translate \0 -> ASCII 0 as replace can't handle ASCII 0 */
3933
case 'Z': /* ^Z must be escaped on Win32 */
3943
if (c == ' ' || c != *++from)
3944
break; /* Found end of string */
3945
*to++=c; /* Copy duplicated separator */
3950
if (*from != ' ' && *from)
3951
die("Wrong string argument in %s", command->query);
3953
while (my_isspace(charset_info,*from)) /* Point to next string */
3956
*to =0; /* End of string marker */
3957
*to_ptr= to+1; /* Store pointer to end */
3960
/* Check if this was a variable */
3963
const char *end= to;
3964
VAR *var=var_get(start, &end, 0, 1);
3965
if (var && to == (char*) end+1)
3967
DBUG_PRINT("info",("var: '%s' -> '%s'", start, var->str_val));
3968
DBUG_RETURN(var->str_val); /* return found variable value */
3975
void set_reconnect(MYSQL* mysql, int val)
3977
my_bool reconnect= val;
3978
DBUG_ENTER("set_reconnect");
3979
DBUG_PRINT("info", ("val: %d", val));
3980
#if MYSQL_VERSION_ID < 50000
3981
mysql->reconnect= reconnect;
3983
mysql_options(mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
3989
int select_connection_name(const char *name)
3991
DBUG_ENTER("select_connection_name");
3992
DBUG_PRINT("enter",("name: '%s'", name));
3994
if (!(cur_con= find_connection_by_name(name)))
3995
die("connection '%s' not found in connection pool", name);
3997
/* Update $mysql_get_server_version to that of current connection */
3998
var_set_mysql_get_server_version(&cur_con->mysql);
4004
int select_connection(struct st_command *command)
4007
char *p= command->first_argument;
4008
DBUG_ENTER("select_connection");
4011
die("Missing connection name in connect");
4013
while (*p && !my_isspace(charset_info,*p))
4017
command->last_argument= p;
4018
DBUG_RETURN(select_connection_name(name));
4022
void do_close_connection(struct st_command *command)
4024
char *p= command->first_argument, *name;
4025
struct st_connection *con;
4027
DBUG_ENTER("close_connection");
4028
DBUG_PRINT("enter",("name: '%s'",p));
4031
die("Missing connection name in disconnect");
4033
while (*p && !my_isspace(charset_info,*p))
4038
command->last_argument= p;
4040
if (!(con= find_connection_by_name(name)))
4041
die("connection '%s' not found in connection pool", name);
4043
DBUG_PRINT("info", ("Closing connection %s", con->name));
4044
#ifndef EMBEDDED_LIBRARY
4045
if (command->type == Q_DIRTY_CLOSE)
4047
if (con->mysql.net.vio)
4049
vio_delete(con->mysql.net.vio);
4050
con->mysql.net.vio = 0;
4055
As query could be still executed in a separate theread
4056
we need to check if the query's thread was finished and probably wait
4057
(embedded-server specific)
4059
wait_query_thread_end(con);
4060
#endif /*EMBEDDED_LIBRARY*/
4062
mysql_stmt_close(con->stmt);
4065
mysql_close(&con->mysql);
4067
if (con->util_mysql)
4068
mysql_close(con->util_mysql);
4071
my_free(con->name, MYF(0));
4074
When the connection is closed set name to "-closed_connection-"
4075
to make it possible to reuse the connection name.
4077
if (!(con->name = my_strdup("-closed_connection-", MYF(MY_WME))))
4078
die("Out of memory");
4085
Connect to a server doing several retries if needed.
4089
con - connection structure to be used
4090
host, user, pass, - connection parameters
4095
Sometimes in a test the client starts before
4096
the server - to solve the problem, we try again
4097
after some sleep if connection fails the first
4100
This function will try to connect to the given server
4101
"opt_max_connect_retries" times and sleep "connection_retry_sleep"
4102
seconds between attempts before finally giving up.
4103
This helps in situation when the client starts
4104
before the server (which happens sometimes).
4105
It will only ignore connection errors during these retries.
4109
void safe_connect(MYSQL* mysql, const char *name, const char *host,
4110
const char *user, const char *pass, const char *db,
4113
int failed_attempts= 0;
4114
static ulong connection_retry_sleep= 100000; /* Microseconds */
4116
DBUG_ENTER("safe_connect");
4117
while(!mysql_real_connect(mysql, host, user, pass, db, port, NULL,
4118
CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS))
4123
Only allow retry if this was an error indicating the server
4124
could not be contacted. Error code differs depending
4125
on protocol/connection type
4128
if ((mysql_errno(mysql) == CR_CONN_HOST_ERROR ||
4129
mysql_errno(mysql) == CR_CONNECTION_ERROR) &&
4130
failed_attempts < opt_max_connect_retries)
4132
verbose_msg("Connect attempt %d/%d failed: %d: %s", failed_attempts,
4133
opt_max_connect_retries, mysql_errno(mysql),
4134
mysql_error(mysql));
4135
my_sleep(connection_retry_sleep);
4139
if (failed_attempts > 0)
4140
die("Could not open connection '%s' after %d attempts: %d %s", name,
4141
failed_attempts, mysql_errno(mysql), mysql_error(mysql));
4143
die("Could not open connection '%s': %d %s", name,
4144
mysql_errno(mysql), mysql_error(mysql));
4153
Connect to a server and handle connection errors in case they occur.
4156
connect_n_handle_errors()
4157
q - context of connect "query" (command)
4158
con - connection structure to be used
4159
host, user, pass, - connection parameters
4163
This function will try to establish a connection to server and handle
4164
possible errors in the same manner as if "connect" was usual SQL-statement
4165
(If error is expected it will ignore it once it occurs and log the
4166
"statement" to the query log).
4167
Unlike safe_connect() it won't do several attempts.
4175
int connect_n_handle_errors(struct st_command *command,
4176
MYSQL* con, const char* host,
4177
const char* user, const char* pass,
4178
const char* db, int port, const char* sock)
4184
/* Only log if an error is expected */
4185
if (!command->abort_on_error &&
4189
Log the connect to result log
4191
dynstr_append_mem(ds, "connect(", 8);
4192
replace_dynstr_append(ds, host);
4193
dynstr_append_mem(ds, ",", 1);
4194
replace_dynstr_append(ds, user);
4195
dynstr_append_mem(ds, ",", 1);
4196
replace_dynstr_append(ds, pass);
4197
dynstr_append_mem(ds, ",", 1);
4199
replace_dynstr_append(ds, db);
4200
dynstr_append_mem(ds, ",", 1);
4201
replace_dynstr_append_uint(ds, port);
4202
dynstr_append_mem(ds, ",", 1);
4204
replace_dynstr_append(ds, sock);
4205
dynstr_append_mem(ds, ")", 1);
4206
dynstr_append_mem(ds, delimiter, delimiter_length);
4207
dynstr_append_mem(ds, "\n", 1);
4209
if (!mysql_real_connect(con, host, user, pass, db, port, 0,
4210
CLIENT_MULTI_STATEMENTS))
4212
var_set_errno(mysql_errno(con));
4213
handle_error(command, mysql_errno(con), mysql_error(con),
4214
mysql_sqlstate(con), ds);
4215
return 0; /* Not connected */
4219
handle_no_error(command);
4220
return 1; /* Connected */
4225
Open a new connection to MySQL Server with the parameters
4226
specified. Make the new connection the current connection.
4233
connect(<name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]]);
4234
connect <name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]];
4236
<name> - name of the new connection
4237
<host> - hostname of server
4238
<user> - user to connect as
4239
<pass> - password used when connecting
4240
<db> - initial db when connected
4241
<port> - server port
4242
<sock> - server socket
4243
<opts> - options to use for the connection
4244
* SSL - use SSL if available
4245
* COMPRESS - use compression if available
4249
void do_connect(struct st_command *command)
4251
int con_port= opt_port;
4253
my_bool con_ssl= 0, con_compress= 0;
4254
struct st_connection* con_slot;
4256
static DYNAMIC_STRING ds_connection_name;
4257
static DYNAMIC_STRING ds_host;
4258
static DYNAMIC_STRING ds_user;
4259
static DYNAMIC_STRING ds_password;
4260
static DYNAMIC_STRING ds_database;
4261
static DYNAMIC_STRING ds_port;
4262
static DYNAMIC_STRING ds_sock;
4263
static DYNAMIC_STRING ds_options;
4264
const struct command_arg connect_args[] = {
4265
{ "connection name", ARG_STRING, TRUE, &ds_connection_name, "Name of the connection" },
4266
{ "host", ARG_STRING, TRUE, &ds_host, "Host to connect to" },
4267
{ "user", ARG_STRING, FALSE, &ds_user, "User to connect as" },
4268
{ "passsword", ARG_STRING, FALSE, &ds_password, "Password used when connecting" },
4269
{ "database", ARG_STRING, FALSE, &ds_database, "Database to select after connect" },
4270
{ "port", ARG_STRING, FALSE, &ds_port, "Port to connect to" },
4271
{ "socket", ARG_STRING, FALSE, &ds_sock, "Socket to connect with" },
4272
{ "options", ARG_STRING, FALSE, &ds_options, "Options to use while connecting" }
4275
DBUG_ENTER("do_connect");
4276
DBUG_PRINT("enter",("connect: %s", command->first_argument));
4278
strip_parentheses(command);
4279
check_command_args(command, command->first_argument, connect_args,
4280
sizeof(connect_args)/sizeof(struct command_arg),
4286
con_port= atoi(ds_port.str);
4288
die("Illegal argument for port: '%s'", ds_port.str);
4295
If the socket is specified just as a name without path
4296
append tmpdir in front
4298
if (*ds_sock.str != FN_LIBCHAR)
4300
char buff[FN_REFLEN];
4301
fn_format(buff, ds_sock.str, TMPDIR, "", 0);
4302
dynstr_set(&ds_sock, buff);
4307
/* No socket specified, use default */
4308
dynstr_set(&ds_sock, unix_sock);
4310
DBUG_PRINT("info", ("socket: %s", ds_sock.str));
4314
con_options= ds_options.str;
4315
while (*con_options)
4318
/* Step past any spaces in beginning of option*/
4319
while (*con_options && my_isspace(charset_info, *con_options))
4321
/* Find end of this option */
4323
while (*end && !my_isspace(charset_info, *end))
4325
if (!strncmp(con_options, "SSL", 3))
4327
else if (!strncmp(con_options, "COMPRESS", 8))
4330
die("Illegal option to connect: %.*s",
4331
(int) (end - con_options), con_options);
4332
/* Process next option */
4336
if (find_connection_by_name(ds_connection_name.str))
4337
die("Connection %s already exists", ds_connection_name.str);
4339
if (next_con != connections_end)
4343
if (!(con_slot= find_connection_by_name("-closed_connection-")))
4344
die("Connection limit exhausted, you can have max %d connections",
4345
(int) (sizeof(connections)/sizeof(struct st_connection)));
4348
#ifdef EMBEDDED_LIBRARY
4349
con_slot->query_done= 1;
4351
if (!mysql_init(&con_slot->mysql))
4352
die("Failed on mysql_init()");
4353
if (opt_compress || con_compress)
4354
mysql_options(&con_slot->mysql, MYSQL_OPT_COMPRESS, NullS);
4355
mysql_options(&con_slot->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
4356
mysql_options(&con_slot->mysql, MYSQL_SET_CHARSET_NAME,
4357
charset_info->csname);
4358
int opt_protocol= MYSQL_PROTOCOL_TCP;
4359
mysql_options(&con_slot->mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
4360
if (opt_charsets_dir)
4361
mysql_options(&con_slot->mysql, MYSQL_SET_CHARSET_DIR,
4364
/* Use default db name */
4365
if (ds_database.length == 0)
4366
dynstr_set(&ds_database, opt_db);
4368
/* Special database to allow one to connect without a database name */
4369
if (ds_database.length && !strcmp(ds_database.str,"*NO-ONE*"))
4370
dynstr_set(&ds_database, "");
4372
if (connect_n_handle_errors(command, &con_slot->mysql,
4373
ds_host.str,ds_user.str,
4374
ds_password.str, ds_database.str,
4375
con_port, ds_sock.str))
4377
DBUG_PRINT("info", ("Inserting connection %s in connection pool",
4378
ds_connection_name.str));
4379
if (!(con_slot->name= my_strdup(ds_connection_name.str, MYF(MY_WME))))
4380
die("Out of memory");
4383
if (con_slot == next_con)
4384
next_con++; /* if we used the next_con slot, advance the pointer */
4387
/* Update $mysql_get_server_version to that of current connection */
4388
var_set_mysql_get_server_version(&cur_con->mysql);
4390
dynstr_free(&ds_connection_name);
4391
dynstr_free(&ds_host);
4392
dynstr_free(&ds_user);
4393
dynstr_free(&ds_password);
4394
dynstr_free(&ds_database);
4395
dynstr_free(&ds_port);
4396
dynstr_free(&ds_sock);
4397
dynstr_free(&ds_options);
4402
int do_done(struct st_command *command)
4404
/* Check if empty block stack */
4405
if (cur_block == block_stack)
4407
if (*command->query != '}')
4408
die("Stray 'end' command - end of block before beginning");
4409
die("Stray '}' - end of block before beginning");
4412
/* Test if inner block has been executed */
4413
if (cur_block->ok && cur_block->cmd == cmd_while)
4415
/* Pop block from stack, re-execute outer block */
4417
parser.current_line = cur_block->line;
4421
/* Pop block from stack, goto next line */
4423
parser.current_line++;
4430
Process start of a "if" or "while" statement
4448
Evaluates the <expr> and if it evaluates to
4449
greater than zero executes the following code block.
4450
A '!' can be used before the <expr> to indicate it should
4451
be executed if it evaluates to zero.
4455
void do_block(enum block_cmd cmd, struct st_command* command)
4457
char *p= command->first_argument;
4458
const char *expr_start, *expr_end;
4460
const char *cmd_name= (cmd == cmd_while ? "while" : "if");
4461
my_bool not_expr= FALSE;
4462
DBUG_ENTER("do_block");
4463
DBUG_PRINT("enter", ("%s", cmd_name));
4465
/* Check stack overflow */
4466
if (cur_block == block_stack_end)
4467
die("Nesting too deeply");
4469
/* Set way to find outer block again, increase line counter */
4470
cur_block->line= parser.current_line++;
4472
/* If this block is ignored */
4475
/* Inner block should be ignored too */
4477
cur_block->cmd= cmd;
4478
cur_block->ok= FALSE;
4482
/* Parse and evaluate test expression */
4483
expr_start= strchr(p, '(');
4485
die("missing '(' in %s", cmd_name);
4487
/* Check for !<expr> */
4488
if (*expr_start == '!')
4491
expr_start++; /* Step past the '!' */
4493
/* Find ending ')' */
4494
expr_end= strrchr(expr_start, ')');
4496
die("missing ')' in %s", cmd_name);
4497
p= (char*)expr_end+1;
4499
while (*p && my_isspace(charset_info, *p))
4501
if (*p && *p != '{')
4502
die("Missing '{' after %s. Found \"%s\"", cmd_name, p);
4504
var_init(&v,0,0,0,0);
4505
eval_expr(&v, expr_start, &expr_end);
4507
/* Define inner block */
4509
cur_block->cmd= cmd;
4510
cur_block->ok= (v.int_val ? TRUE : FALSE);
4513
cur_block->ok = !cur_block->ok;
4515
DBUG_PRINT("info", ("OK: %d", cur_block->ok));
4522
void do_delimiter(struct st_command* command)
4524
char* p= command->first_argument;
4525
DBUG_ENTER("do_delimiter");
4526
DBUG_PRINT("enter", ("first_argument: %s", command->first_argument));
4528
while (*p && my_isspace(charset_info, *p))
4532
die("Can't set empty delimiter");
4534
strmake(delimiter, p, sizeof(delimiter) - 1);
4535
delimiter_length= strlen(delimiter);
4537
DBUG_PRINT("exit", ("delimiter: %s", delimiter));
4538
command->last_argument= p + delimiter_length;
4543
my_bool match_delimiter(int c, const char *delim, uint length)
4546
char tmp[MAX_DELIMITER_LENGTH];
4551
for (i= 1; i < length &&
4552
(c= my_getc(cur_file->file)) == *(delim + i);
4557
return 1; /* Found delimiter */
4559
/* didn't find delimiter, push back things that we read */
4562
my_ungetc(tmp[--i]);
4567
my_bool end_of_query(int c)
4569
return match_delimiter(c, delimiter, delimiter_length);
4574
Read one "line" from the file
4578
buf buffer for the read line
4579
size size of the buffer i.e max size to read
4582
This function actually reads several lines and adds them to the
4583
buffer buf. It continues to read until it finds what it believes
4584
is a complete query.
4586
Normally that means it will read lines until it reaches the
4587
"delimiter" that marks end of query. Default delimiter is ';'
4588
The function should be smart enough not to detect delimiter's
4589
found inside strings surrounded with '"' and '\'' escaped strings.
4591
If the first line in a query starts with '#' or '-' this line is treated
4592
as a comment. A comment is always terminated when end of line '\n' is
4597
int read_line(char *buf, int size)
4599
char c, last_quote= 0;
4600
char *p= buf, *buf_end= buf + size - 1;
4602
enum {R_NORMAL, R_Q, R_SLASH_IN_Q,
4603
R_COMMENT, R_LINE_START} state= R_LINE_START;
4604
DBUG_ENTER("read_line");
4606
start_lineno= cur_file->lineno;
4607
DBUG_PRINT("info", ("Starting to read at lineno: %d", start_lineno));
4608
for (; p < buf_end ;)
4611
c= my_getc(cur_file->file);
4612
if (feof(cur_file->file))
4615
if (cur_file->file != stdin)
4617
my_fclose(cur_file->file, MYF(0));
4620
my_free((uchar*) cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR));
4621
cur_file->file_name= 0;
4622
if (cur_file == file_stack)
4624
/* We're back at the first file, check if
4625
all { have matching }
4627
if (cur_block != block_stack)
4628
die("Missing end of block");
4631
DBUG_PRINT("info", ("end of file at line %d", cur_file->lineno));
4635
start_lineno= cur_file->lineno;
4641
/* Line counting is independent of state */
4644
/* Convert cr/lf to lf */
4645
if (p != buf && *(p-1) == '\r')
4651
if (end_of_query(c))
4654
DBUG_PRINT("exit", ("Found delimiter '%s' at line %d",
4655
delimiter, cur_file->lineno));
4658
else if ((c == '{' &&
4659
(!my_strnncoll_simple(charset_info, (const uchar*) "while", 5,
4660
(uchar*) buf, min(5, p - buf), 0) ||
4661
!my_strnncoll_simple(charset_info, (const uchar*) "if", 2,
4662
(uchar*) buf, min(2, p - buf), 0))))
4664
/* Only if and while commands can be terminated by { */
4667
DBUG_PRINT("exit", ("Found '{' indicating start of block at line %d",
4671
else if (c == '\'' || c == '"' || c == '`')
4681
/* Comments are terminated by newline */
4683
DBUG_PRINT("exit", ("Found newline in comment at line: %d",
4690
if (c == '#' || c == '-')
4692
/* A # or - in the first position of the line - this is a comment */
4695
else if (my_isspace(charset_info, c))
4697
/* Skip all space at begining of line */
4700
/* Query hasn't started yet */
4701
start_lineno= cur_file->lineno;
4702
DBUG_PRINT("info", ("Query hasn't started yet, start_lineno: %d",
4707
else if (end_of_query(c))
4710
DBUG_PRINT("exit", ("Found delimiter '%s' at line: %d",
4711
delimiter, cur_file->lineno));
4716
/* A "}" need to be by itself in the begining of a line to terminate */
4719
DBUG_PRINT("exit", ("Found '}' in begining of a line at line: %d",
4723
else if (c == '\'' || c == '"' || c == '`')
4733
if (c == last_quote)
4736
state= R_SLASH_IN_Q;
4747
/* Could be a multibyte character */
4748
/* This code is based on the code in "sql_load.cc" */
4750
int charlen = my_mbcharlen(charset_info, c);
4751
/* We give up if multibyte character is started but not */
4752
/* completed before we pass buf_end */
4753
if ((charlen > 1) && (p + charlen) <= buf_end)
4760
for (i= 1; i < charlen; i++)
4762
if (feof(cur_file->file))
4764
c= my_getc(cur_file->file);
4767
if (! my_ismbchar(charset_info, mb_start, p))
4769
/* It was not a multiline char, push back the characters */
4770
/* We leave first 'c', i.e. pretend it was a normal char */
4771
while (p > mb_start)
4780
die("The input buffer is too small for this query.x\n" \
4781
"check your query or increase MAX_QUERY and recompile");
4787
Convert the read query to result format version 1
4789
That is: After newline, all spaces need to be skipped
4790
unless the previous char was a quote
4792
This is due to an old bug that has now been fixed, but the
4793
version 1 output format is preserved by using this function
4797
void convert_to_format_v1(char* query)
4799
int last_c_was_quote= 0;
4800
char *p= query, *to= query;
4801
char *end= strend(query);
4806
if (*p == '\n' && !last_c_was_quote)
4808
*to++ = *p++; /* Save the newline */
4810
/* Skip any spaces on next line */
4811
while (*p && my_isspace(charset_info, *p))
4814
last_c_was_quote= 0;
4816
else if (*p == '\'' || *p == '"' || *p == '`')
4821
/* Copy anything until the next quote of same type */
4822
while (*p && *p != last_c)
4827
last_c_was_quote= 1;
4832
last_c_was_quote= 0;
4839
Check a command that is about to be sent (or should have been
4840
sent if parsing was enabled) to mysql server for
4841
suspicious things and generate warnings.
4844
void scan_command_for_warnings(struct st_command *command)
4846
const char *ptr= command->query;
4847
DBUG_ENTER("scan_command_for_warnings");
4848
DBUG_PRINT("enter", ("query: %s", command->query));
4853
Look for query's that lines that start with a -- comment
4854
and has a mysqltest command
4856
if (ptr[0] == '\n' &&
4857
ptr[1] && ptr[1] == '-' &&
4858
ptr[2] && ptr[2] == '-' &&
4863
char *end, *start= (char*)ptr+3;
4864
/* Skip leading spaces */
4865
while (*start && my_isspace(charset_info, *start))
4868
/* Find end of command(next space) */
4869
while (*end && !my_isspace(charset_info, *end))
4873
DBUG_PRINT("info", ("Checking '%s'", start));
4874
type= find_type(start, &command_typelib, 1+2);
4876
warning_msg("Embedded mysqltest command '--%s' detected in "
4877
"query '%s' was this intentional? ",
4878
start, command->query);
4888
Check for unexpected "junk" after the end of query
4889
This is normally caused by missing delimiters or when
4890
switching between different delimiters
4893
void check_eol_junk_line(const char *line)
4895
const char *p= line;
4896
DBUG_ENTER("check_eol_junk_line");
4897
DBUG_PRINT("enter", ("line: %s", line));
4899
/* Check for extra delimiter */
4900
if (*p && !strncmp(p, delimiter, delimiter_length))
4901
die("Extra delimiter \"%s\" found", delimiter);
4903
/* Allow trailing # comment */
4904
if (*p && *p != '#')
4907
die("Missing delimiter");
4908
die("End of line junk detected: \"%s\"", p);
4913
void check_eol_junk(const char *eol)
4916
DBUG_ENTER("check_eol_junk");
4917
DBUG_PRINT("enter", ("eol: %s", eol));
4919
/* Skip past all spacing chars and comments */
4920
while (*p && (my_isspace(charset_info, *p) || *p == '#' || *p == '\n'))
4922
/* Skip past comments started with # and ended with newline */
4923
if (*p && *p == '#')
4926
while (*p && *p != '\n')
4930
/* Check this line */
4931
if (*p && *p == '\n')
4932
check_eol_junk_line(p);
4938
check_eol_junk_line(p);
4946
Create a command from a set of lines
4950
command_ptr pointer where to return the new query
4953
Converts lines returned by read_line into a command, this involves
4954
parsing the first word in the read line to find the command type.
4956
A -- comment may contain a valid query as the first word after the
4957
comment start. Thus it's always checked to see if that is the case.
4958
The advantage with this approach is to be able to execute commands
4959
terminated by new line '\n' regardless how many "delimiter" it contain.
4962
#define MAX_QUERY (256*1024*2) /* 256K -- a test in sp-big is >128K */
4963
static char read_command_buf[MAX_QUERY];
4965
int read_command(struct st_command** command_ptr)
4967
char *p= read_command_buf;
4968
struct st_command* command;
4969
DBUG_ENTER("read_command");
4971
if (parser.current_line < parser.read_lines)
4973
get_dynamic(&q_lines, (uchar*) command_ptr, parser.current_line) ;
4976
if (!(*command_ptr= command=
4977
(struct st_command*) my_malloc(sizeof(*command),
4978
MYF(MY_WME|MY_ZEROFILL))) ||
4979
insert_dynamic(&q_lines, (uchar*) &command))
4981
command->type= Q_UNKNOWN;
4983
read_command_buf[0]= 0;
4984
if (read_line(read_command_buf, sizeof(read_command_buf)))
4986
check_eol_junk(read_command_buf);
4990
convert_to_format_v1(read_command_buf);
4992
DBUG_PRINT("info", ("query: %s", read_command_buf));
4995
command->type= Q_COMMENT;
4997
else if (p[0] == '-' && p[1] == '-')
4999
command->type= Q_COMMENT_WITH_COMMAND;
5000
p+= 2; /* Skip past -- */
5003
/* Skip leading spaces */
5004
while (*p && my_isspace(charset_info, *p))
5007
if (!(command->query_buf= command->query= my_strdup(p, MYF(MY_WME))))
5008
die("Out of memory");
5010
/* Calculate first word length(the command), terminated by space or ( */
5012
while (*p && !my_isspace(charset_info, *p) && *p != '(')
5014
command->first_word_len= (uint) (p - command->query);
5015
DBUG_PRINT("info", ("first_word: %.*s",
5016
command->first_word_len, command->query));
5018
/* Skip spaces between command and first argument */
5019
while (*p && my_isspace(charset_info, *p))
5021
command->first_argument= p;
5023
command->end= strend(command->query);
5024
command->query_len= (command->end - command->query);
5025
parser.read_lines++;
5030
static struct my_option my_long_options[] =
5032
{"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
5034
{"basedir", 'b', "Basedir for tests.", (uchar**) &opt_basedir,
5035
(uchar**) &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
5036
{"character-sets-dir", OPT_CHARSETS_DIR,
5037
"Directory where character sets are.", (uchar**) &opt_charsets_dir,
5038
(uchar**) &opt_charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
5039
{"compress", 'C', "Use the compressed server/client protocol.",
5040
(uchar**) &opt_compress, (uchar**) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
5042
{"cursor-protocol", OPT_CURSOR_PROTOCOL, "Use cursors for prepared statements.",
5043
(uchar**) &cursor_protocol, (uchar**) &cursor_protocol, 0,
5044
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
5045
{"database", 'D', "Database to use.", (uchar**) &opt_db, (uchar**) &opt_db, 0,
5046
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
5048
{"debug", '#', "This is a non-debug version. Catch this and exit",
5049
0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
5051
{"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
5052
0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
5054
{"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.",
5055
(uchar**) &debug_check_flag, (uchar**) &debug_check_flag, 0,
5056
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
5057
{"debug-info", OPT_DEBUG_INFO, "Print some debug info at exit.",
5058
(uchar**) &debug_info_flag, (uchar**) &debug_info_flag,
5059
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
5060
{"host", 'h', "Connect to host.", (uchar**) &opt_host, (uchar**) &opt_host, 0,
5061
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
5062
{"include", 'i', "Include SQL before each test case.", (uchar**) &opt_include,
5063
(uchar**) &opt_include, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
5064
{"logdir", OPT_LOG_DIR, "Directory for log files", (uchar**) &opt_logdir,
5065
(uchar**) &opt_logdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
5066
{"mark-progress", OPT_MARK_PROGRESS,
5067
"Write linenumber and elapsed time to <testname>.progress ",
5068
(uchar**) &opt_mark_progress, (uchar**) &opt_mark_progress, 0,
5069
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
5070
{"max-connect-retries", OPT_MAX_CONNECT_RETRIES,
5071
"Max number of connection attempts when connecting to server",
5072
(uchar**) &opt_max_connect_retries, (uchar**) &opt_max_connect_retries, 0,
5073
GET_INT, REQUIRED_ARG, 500, 1, 10000, 0, 0, 0},
5074
{"password", 'p', "Password to use when connecting to server.",
5075
0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
5076
{"port", 'P', "Port number to use for connection or 0 for default to, in "
5077
"order of preference, my.cnf, $MYSQL_TCP_PORT, "
5078
#if MYSQL_PORT_DEFAULT == 0
5081
"built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
5082
(uchar**) &opt_port,
5083
(uchar**) &opt_port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
5084
{"protocol", OPT_MYSQL_PROTOCOL,
5085
"The protocol of connection (tcp,socket,pipe,memory).",
5086
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
5087
{"ps-protocol", OPT_PS_PROTOCOL, "Use prepared statements protocol for communication",
5088
(uchar**) &ps_protocol, (uchar**) &ps_protocol, 0,
5089
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
5090
{"quiet", 's', "Suppress all normal output.", (uchar**) &silent,
5091
(uchar**) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
5092
{"record", 'r', "Record output of test_file into result file.",
5093
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
5094
{"result-file", 'R', "Read/Store result from/in this file.",
5095
(uchar**) &result_file_name, (uchar**) &result_file_name, 0,
5096
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
5097
{"server-arg", 'A', "Send option value to embedded server as a parameter.",
5098
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
5099
{"server-file", 'F', "Read embedded server arguments from file.",
5100
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
5101
{"silent", 's', "Suppress all normal output. Synonym for --quiet.",
5102
(uchar**) &silent, (uchar**) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
5103
{"sleep", 'T', "Sleep always this many seconds on sleep commands.",
5104
(uchar**) &opt_sleep, (uchar**) &opt_sleep, 0, GET_INT, REQUIRED_ARG, -1, -1, 0,
5106
{"socket", 'S', "Socket file to use for connection.",
5107
(uchar**) &unix_sock, (uchar**) &unix_sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0,
5109
{"sp-protocol", OPT_SP_PROTOCOL, "Use stored procedures for select",
5110
(uchar**) &sp_protocol, (uchar**) &sp_protocol, 0,
5111
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
5112
{"tail-lines", OPT_TAIL_LINES,
5113
"Number of lines of the resul to include in a failure report",
5114
(uchar**) &opt_tail_lines, (uchar**) &opt_tail_lines, 0,
5115
GET_INT, REQUIRED_ARG, 0, 0, 10000, 0, 0, 0},
5116
{"test-file", 'x', "Read test from/in this file (default stdin).",
5117
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
5118
{"timer-file", 'm', "File where the timing in micro seconds is stored.",
5119
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
5120
{"tmpdir", 't', "Temporary directory where sockets are put.",
5121
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
5122
{"user", 'u', "User for login.", (uchar**) &opt_user, (uchar**) &opt_user, 0,
5123
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
5124
{"verbose", 'v', "Write more.", (uchar**) &verbose, (uchar**) &verbose, 0,
5125
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
5126
{"version", 'V', "Output version information and exit.",
5127
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
5128
{"view-protocol", OPT_VIEW_PROTOCOL, "Use views for select",
5129
(uchar**) &view_protocol, (uchar**) &view_protocol, 0,
5130
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
5131
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
5135
#include <help_start.h>
5137
void print_version(void)
5139
printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname,MTEST_VERSION,
5140
MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE);
5146
printf("MySQL AB, by Sasha, Matt, Monty & Jani\n");
5147
printf("This software comes with ABSOLUTELY NO WARRANTY\n\n");
5148
printf("Runs a test against the mysql server and compares output with a results file.\n\n");
5149
printf("Usage: %s [OPTIONS] [database] < test_file\n", my_progname);
5150
my_print_help(my_long_options);
5151
printf(" --no-defaults Don't read default options from any options file.\n");
5152
my_print_variables(my_long_options);
5155
#include <help_end.h>
5159
Read arguments for embedded server and put them into
5160
embedded_server_args[]
5163
void read_embedded_server_arguments(const char *name)
5165
char argument[1024],buff[FN_REFLEN], *str=0;
5168
if (!test_if_hard_path(name))
5170
strxmov(buff, opt_basedir, name, NullS);
5173
fn_format(buff, name, "", "", MY_UNPACK_FILENAME);
5175
if (!embedded_server_arg_count)
5177
embedded_server_arg_count=1;
5178
embedded_server_args[0]= (char*) ""; /* Progname */
5180
if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
5181
die("Failed to open file '%s'", buff);
5183
while (embedded_server_arg_count < MAX_EMBEDDED_SERVER_ARGS &&
5184
(str=fgets(argument,sizeof(argument), file)))
5186
*(strend(str)-1)=0; /* Remove end newline */
5187
if (!(embedded_server_args[embedded_server_arg_count]=
5188
(char*) my_strdup(str,MYF(MY_WME))))
5190
my_fclose(file,MYF(0));
5191
die("Out of memory");
5194
embedded_server_arg_count++;
5196
my_fclose(file,MYF(0));
5198
die("Too many arguments in option file: %s",name);
5205
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
5211
DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysqltest.trace");
5212
debug_check_flag= 1;
5220
char buff[FN_REFLEN];
5221
if (!test_if_hard_path(argument))
5223
strxmov(buff, opt_basedir, argument, NullS);
5226
fn_format(buff, argument, "", "", MY_UNPACK_FILENAME);
5227
DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0);
5228
if (!(cur_file->file=
5229
my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0))))
5230
die("Could not open '%s' for reading: errno = %d", buff, errno);
5231
cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
5232
cur_file->lineno= 1;
5237
static char buff[FN_REFLEN];
5238
if (!test_if_hard_path(argument))
5240
strxmov(buff, opt_basedir, argument, NullS);
5243
fn_format(buff, argument, "", "", MY_UNPACK_FILENAME);
5245
unlink(timer_file); /* Ignore error, may not exist */
5251
my_free(opt_pass, MYF(MY_ALLOW_ZERO_PTR));
5252
opt_pass= my_strdup(argument, MYF(MY_FAE));
5253
while (*argument) *argument++= 'x'; /* Destroy argument */
5260
strnmov(TMPDIR, argument, sizeof(TMPDIR));
5263
if (!embedded_server_arg_count)
5265
embedded_server_arg_count=1;
5266
embedded_server_args[0]= (char*) "";
5268
if (embedded_server_arg_count == MAX_EMBEDDED_SERVER_ARGS-1 ||
5269
!(embedded_server_args[embedded_server_arg_count++]=
5270
my_strdup(argument, MYF(MY_FAE))))
5272
die("Can't use server argument");
5276
read_embedded_server_arguments(argument);
5289
int parse_args(int argc, char **argv)
5291
load_defaults("my",load_default_groups,&argc,&argv);
5294
if ((handle_options(&argc, &argv, my_long_options, get_one_option)))
5305
opt_pass= get_tty_password(NullS); /* purify tested */
5306
if (debug_info_flag)
5307
my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
5308
if (debug_check_flag)
5309
my_end_arg= MY_CHECK_ERROR;
5315
Write the content of str into file
5319
fname - name of file to truncate/create and write to
5320
str - content to write to file
5321
size - size of content witten to file
5322
append - append to file instead of overwriting old file
5325
void str_to_file2(const char *fname, char *str, int size, my_bool append)
5328
char buff[FN_REFLEN];
5329
int flags= O_WRONLY | O_CREAT;
5330
if (!test_if_hard_path(fname))
5332
strxmov(buff, opt_basedir, fname, NullS);
5335
fn_format(buff, fname, "", "", MY_UNPACK_FILENAME);
5339
if ((fd= my_open(buff, flags,
5340
MYF(MY_WME | MY_FFNF))) < 0)
5341
die("Could not open '%s' for writing: errno = %d", buff, errno);
5342
if (append && my_seek(fd, 0, SEEK_END, MYF(0)) == MY_FILEPOS_ERROR)
5343
die("Could not find end of file '%s': errno = %d", buff, errno);
5344
if (my_write(fd, (uchar*)str, size, MYF(MY_WME|MY_FNABP)))
5345
die("write failed");
5346
my_close(fd, MYF(0));
5350
Write the content of str into file
5354
fname - name of file to truncate/create and write to
5355
str - content to write to file
5356
size - size of content witten to file
5359
void str_to_file(const char *fname, char *str, int size)
5361
str_to_file2(fname, str, size, FALSE);
5365
void dump_result_to_log_file(char *buf, int size)
5367
char log_file[FN_REFLEN];
5368
str_to_file(fn_format(log_file, result_file_name, opt_logdir, ".log",
5369
*opt_logdir ? MY_REPLACE_DIR | MY_REPLACE_EXT :
5372
fprintf(stderr, "\nMore results from queries before failure can be found in %s\n",
5376
void dump_progress(void)
5378
char progress_file[FN_REFLEN];
5379
str_to_file(fn_format(progress_file, result_file_name,
5380
opt_logdir, ".progress",
5381
*opt_logdir ? MY_REPLACE_DIR | MY_REPLACE_EXT :
5383
ds_progress.str, ds_progress.length);
5386
void dump_warning_messages(void)
5388
char warn_file[FN_REFLEN];
5390
str_to_file(fn_format(warn_file, result_file_name, opt_logdir, ".warnings",
5391
*opt_logdir ? MY_REPLACE_DIR | MY_REPLACE_EXT :
5393
ds_warning_messages.str, ds_warning_messages.length);
5396
void check_regerr(my_regex_t* r, int err)
5402
my_regerror(err,r,err_buf,sizeof(err_buf));
5403
die("Regex error: %s\n", err_buf);
5410
DYNAMIC_ARRAY patterns;
5413
init_win_path_patterns
5416
Setup string patterns that will be used to detect filenames that
5417
needs to be converted from Win to Unix format
5421
void init_win_path_patterns()
5423
/* List of string patterns to match in order to find paths */
5424
const char* paths[] = { "$MYSQL_TEST_DIR",
5426
"$MYSQLTEST_VARDIR",
5428
int num_paths= sizeof(paths)/sizeof(char*);
5432
DBUG_ENTER("init_win_path_patterns");
5434
my_init_dynamic_array(&patterns, sizeof(const char*), 16, 16);
5436
/* Loop through all paths in the array */
5437
for (i= 0; i < num_paths; i++)
5440
if (*(paths[i]) == '$')
5442
v= var_get(paths[i], 0, 0, 0);
5443
p= my_strdup(v->str_val, MYF(MY_FAE));
5446
p= my_strdup(paths[i], MYF(MY_FAE));
5448
/* Don't insert zero length strings in patterns array */
5455
if (insert_dynamic(&patterns, (uchar*) &p))
5458
DBUG_PRINT("info", ("p: %s", p));
5469
void free_win_path_patterns()
5472
for (i=0 ; i < patterns.elements ; i++)
5474
const char** pattern= dynamic_element(&patterns, i, const char**);
5475
my_free((char*) *pattern, MYF(0));
5477
delete_dynamic(&patterns);
5484
Search the string 'val' for the patterns that are known to be
5485
strings that contain filenames. Convert all \ to / in the
5486
filenames that are found.
5489
val = 'Error "c:\mysql\mysql-test\var\test\t1.frm" didn't exist'
5490
=> $MYSQL_TEST_DIR is found by strstr
5491
=> all \ from c:\mysql\m... until next space is converted into /
5494
void fix_win_paths(const char *val, int len)
5499
DBUG_ENTER("fix_win_paths");
5500
for (i= 0; i < patterns.elements; i++)
5502
const char** pattern= dynamic_element(&patterns, i, const char**);
5503
DBUG_PRINT("info", ("pattern: %s", *pattern));
5505
/* Search for the path in string */
5506
while ((p= strstr(val, *pattern)))
5508
DBUG_PRINT("info", ("Found %s in val p: %s", *pattern, p));
5510
while (*p && !my_isspace(charset_info, *p))
5516
DBUG_PRINT("info", ("Converted \\ to /, p: %s", p));
5519
DBUG_PRINT("exit", (" val: %s, len: %d", val, len));
5527
Append the result for one field to the dynamic string ds
5530
void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD* field,
5531
const char* val, ulonglong len, my_bool is_null)
5533
if (col_idx < max_replace_column && replace_column[col_idx])
5535
val= replace_column[col_idx];
5544
else if ((field->type == MYSQL_TYPE_DOUBLE ||
5545
field->type == MYSQL_TYPE_FLOAT ) &&
5546
field->decimals >= 31)
5548
/* Convert 1.2e+018 to 1.2e+18 and 1.2e-018 to 1.2e-18 */
5549
char *start= strchr(val, 'e');
5550
if (start && strlen(start) >= 5 &&
5551
(start[1] == '-' || start[1] == '+') && start[2] == '0')
5553
start+=2; /* Now points at first '0' */
5554
/* Move all chars after the first '0' one step left */
5555
memmove(start, start + 1, strlen(start));
5561
if (!display_result_vertically)
5564
dynstr_append_mem(ds, "\t", 1);
5565
replace_dynstr_append_mem(ds, val, (int)len);
5569
dynstr_append(ds, field->name);
5570
dynstr_append_mem(ds, "\t", 1);
5571
replace_dynstr_append_mem(ds, val, (int)len);
5572
dynstr_append_mem(ds, "\n", 1);
5578
Append all results to the dynamic string separated with '\t'
5579
Values may be converted with 'replace_column'
5582
void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
5585
uint num_fields= mysql_num_fields(res);
5586
MYSQL_FIELD *fields= mysql_fetch_fields(res);
5589
while ((row = mysql_fetch_row(res)))
5592
lengths = mysql_fetch_lengths(res);
5593
for (i = 0; i < num_fields; i++)
5594
append_field(ds, i, &fields[i],
5595
(const char*)row[i], lengths[i], !row[i]);
5596
if (!display_result_vertically)
5597
dynstr_append_mem(ds, "\n", 1);
5603
Append all results from ps execution to the dynamic string separated
5604
with '\t'. Values may be converted with 'replace_column'
5607
void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt,
5608
MYSQL_FIELD *fields, uint num_fields)
5610
MYSQL_BIND *my_bind;
5615
/* Allocate array with bind structs, lengths and NULL flags */
5616
my_bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND),
5617
MYF(MY_WME | MY_FAE | MY_ZEROFILL));
5618
length= (ulong*) my_malloc(num_fields * sizeof(ulong),
5619
MYF(MY_WME | MY_FAE));
5620
is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool),
5621
MYF(MY_WME | MY_FAE));
5623
/* Allocate data for the result of each field */
5624
for (i= 0; i < num_fields; i++)
5626
uint max_length= fields[i].max_length + 1;
5627
my_bind[i].buffer_type= MYSQL_TYPE_STRING;
5628
my_bind[i].buffer= (char *)my_malloc(max_length, MYF(MY_WME | MY_FAE));
5629
my_bind[i].buffer_length= max_length;
5630
my_bind[i].is_null= &is_null[i];
5631
my_bind[i].length= &length[i];
5633
DBUG_PRINT("bind", ("col[%d]: buffer_type: %d, buffer_length: %lu",
5634
i, my_bind[i].buffer_type, my_bind[i].buffer_length));
5637
if (mysql_stmt_bind_result(stmt, my_bind))
5638
die("mysql_stmt_bind_result failed: %d: %s",
5639
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
5641
while (mysql_stmt_fetch(stmt) == 0)
5643
for (i= 0; i < num_fields; i++)
5644
append_field(ds, i, &fields[i], (const char *) my_bind[i].buffer,
5645
*my_bind[i].length, *my_bind[i].is_null);
5646
if (!display_result_vertically)
5647
dynstr_append_mem(ds, "\n", 1);
5650
if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
5651
die("fetch didn't end with MYSQL_NO_DATA from statement: %d %s",
5652
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
5654
for (i= 0; i < num_fields; i++)
5656
/* Free data for output */
5657
my_free(my_bind[i].buffer, MYF(MY_WME | MY_FAE));
5659
/* Free array with bind structs, lengths and NULL flags */
5660
my_free(my_bind , MYF(MY_WME | MY_FAE));
5661
my_free(length , MYF(MY_WME | MY_FAE));
5662
my_free(is_null , MYF(MY_WME | MY_FAE));
5667
Append metadata for fields to output
5670
void append_metadata(DYNAMIC_STRING *ds,
5674
MYSQL_FIELD *field_end;
5675
dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t"
5676
"Column_alias\tType\tLength\tMax length\tIs_null\t"
5677
"Flags\tDecimals\tCharsetnr\n");
5679
for (field_end= field+num_fields ;
5683
dynstr_append_mem(ds, field->catalog,
5684
field->catalog_length);
5685
dynstr_append_mem(ds, "\t", 1);
5686
dynstr_append_mem(ds, field->db, field->db_length);
5687
dynstr_append_mem(ds, "\t", 1);
5688
dynstr_append_mem(ds, field->org_table,
5689
field->org_table_length);
5690
dynstr_append_mem(ds, "\t", 1);
5691
dynstr_append_mem(ds, field->table,
5692
field->table_length);
5693
dynstr_append_mem(ds, "\t", 1);
5694
dynstr_append_mem(ds, field->org_name,
5695
field->org_name_length);
5696
dynstr_append_mem(ds, "\t", 1);
5697
dynstr_append_mem(ds, field->name, field->name_length);
5698
dynstr_append_mem(ds, "\t", 1);
5699
replace_dynstr_append_uint(ds, field->type);
5700
dynstr_append_mem(ds, "\t", 1);
5701
replace_dynstr_append_uint(ds, field->length);
5702
dynstr_append_mem(ds, "\t", 1);
5703
replace_dynstr_append_uint(ds, field->max_length);
5704
dynstr_append_mem(ds, "\t", 1);
5705
dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ?
5707
dynstr_append_mem(ds, "\t", 1);
5708
replace_dynstr_append_uint(ds, field->flags);
5709
dynstr_append_mem(ds, "\t", 1);
5710
replace_dynstr_append_uint(ds, field->decimals);
5711
dynstr_append_mem(ds, "\t", 1);
5712
replace_dynstr_append_uint(ds, field->charsetnr);
5713
dynstr_append_mem(ds, "\n", 1);
5719
Append affected row count and other info to output
5722
void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows,
5725
char buf[40], buff2[21];
5726
sprintf(buf,"affected rows: %s\n", llstr(affected_rows, buff2));
5727
dynstr_append(ds, buf);
5730
dynstr_append(ds, "info: ");
5731
dynstr_append(ds, info);
5732
dynstr_append_mem(ds, "\n", 1);
5738
Display the table headings with the names tab separated
5741
void append_table_headings(DYNAMIC_STRING *ds,
5746
for (col_idx= 0; col_idx < num_fields; col_idx++)
5749
dynstr_append_mem(ds, "\t", 1);
5750
replace_dynstr_append(ds, field[col_idx].name);
5752
dynstr_append_mem(ds, "\n", 1);
5756
Fetch warnings from server and append to ds
5759
Number of warnings appended to ds
5762
int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql)
5765
MYSQL_RES *warn_res;
5766
DBUG_ENTER("append_warnings");
5768
if (!(count= mysql_warning_count(mysql)))
5772
If one day we will support execution of multi-statements
5773
through PS API we should not issue SHOW WARNINGS until
5774
we have not read all results...
5776
DBUG_ASSERT(!mysql_more_results(mysql));
5778
if (mysql_real_query(mysql, "SHOW WARNINGS", 13))
5779
die("Error running query \"SHOW WARNINGS\": %s", mysql_error(mysql));
5781
if (!(warn_res= mysql_store_result(mysql)))
5782
die("Warning count is %u but didn't get any warnings",
5785
append_result(ds, warn_res);
5786
mysql_free_result(warn_res);
5788
DBUG_PRINT("warnings", ("%s", ds->str));
5795
Run query using MySQL C API
5800
command current command pointer
5801
flags flags indicating if we should SEND and/or REAP
5802
query query string to execute
5803
query_len length query string to execute
5804
ds output buffer where to store result form query
5807
void run_query_normal(struct st_connection *cn, struct st_command *command,
5808
int flags, char *query, int query_len,
5809
DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings)
5812
MYSQL *mysql= &cn->mysql;
5813
int err= 0, counter= 0;
5814
DBUG_ENTER("run_query_normal");
5815
DBUG_PRINT("enter",("flags: %d", flags));
5816
DBUG_PRINT("enter", ("query: '%-.60s'", query));
5818
if (flags & QUERY_SEND_FLAG)
5823
if (do_send_query(cn, query, query_len, flags))
5825
handle_error(command, mysql_errno(mysql), mysql_error(mysql),
5826
mysql_sqlstate(mysql), ds);
5830
#ifdef EMBEDDED_LIBRARY
5832
Here we handle 'reap' command, so we need to check if the
5833
query's thread was finished and probably wait
5835
else if (flags & QUERY_REAP_FLAG)
5836
wait_query_thread_end(cn);
5837
#endif /*EMBEDDED_LIBRARY*/
5838
if (!(flags & QUERY_REAP_FLAG))
5844
When on first result set, call mysql_read_query_result to retrieve
5845
answer to the query sent earlier
5847
if ((counter==0) && mysql_read_query_result(mysql))
5849
handle_error(command, mysql_errno(mysql), mysql_error(mysql),
5850
mysql_sqlstate(mysql), ds);
5856
Store the result of the query if it will return any fields
5858
if (mysql_field_count(mysql) && ((res= mysql_store_result(mysql)) == 0))
5860
handle_error(command, mysql_errno(mysql), mysql_error(mysql),
5861
mysql_sqlstate(mysql), ds);
5865
if (!disable_result_log)
5867
ulonglong affected_rows= 0; /* Ok to be undef if 'disable_info' is set */
5871
MYSQL_FIELD *fields= mysql_fetch_fields(res);
5872
uint num_fields= mysql_num_fields(res);
5874
if (display_metadata)
5875
append_metadata(ds, fields, num_fields);
5877
if (!display_result_vertically)
5878
append_table_headings(ds, fields, num_fields);
5880
append_result(ds, res);
5884
Need to call mysql_affected_rows() before the "new"
5885
query to find the warnings
5888
affected_rows= mysql_affected_rows(mysql);
5891
Add all warnings to the result. We can't do this if we are in
5892
the middle of processing results from multi-statement, because
5893
this will break protocol.
5895
if (!disable_warnings && !mysql_more_results(mysql))
5897
if (append_warnings(ds_warnings, mysql) || ds_warnings->length)
5899
dynstr_append_mem(ds, "Warnings:\n", 10);
5900
dynstr_append_mem(ds, ds_warnings->str, ds_warnings->length);
5905
append_info(ds, affected_rows, mysql_info(mysql));
5910
mysql_free_result(res);
5914
} while (!(err= mysql_next_result(mysql)));
5917
/* We got an error from mysql_next_result, maybe expected */
5918
handle_error(command, mysql_errno(mysql), mysql_error(mysql),
5919
mysql_sqlstate(mysql), ds);
5922
DBUG_ASSERT(err == -1); /* Successful and there are no more results */
5924
/* If we come here the query is both executed and read successfully */
5925
handle_no_error(command);
5930
We save the return code (mysql_errno(mysql)) from the last call sent
5931
to the server into the mysqltest builtin variable $mysql_errno. This
5932
variable then can be used from the test case itself.
5934
var_set_errno(mysql_errno(mysql));
5940
Handle errors which occurred during execution
5945
err_errno - error number
5946
err_error - error message
5947
err_sqlstate - sql state
5948
ds - dynamic string which is used for output buffer
5951
If there is an unexpected error this function will abort mysqltest
5955
void handle_error(struct st_command *command,
5956
unsigned int err_errno, const char *err_error,
5957
const char *err_sqlstate, DYNAMIC_STRING *ds)
5961
DBUG_ENTER("handle_error");
5963
if (command->require_file[0])
5966
The query after a "--require" failed. This is fine as long the server
5967
returned a valid reponse. Don't allow 2013 or 2006 to trigger an
5968
abort_not_supported_test
5970
if (err_errno == CR_SERVER_LOST ||
5971
err_errno == CR_SERVER_GONE_ERROR)
5972
die("require query '%s' failed: %d: %s", command->query,
5973
err_errno, err_error);
5975
/* Abort the run of this test, pass the failed query as reason */
5976
abort_not_supported_test("Query '%s' failed, required functionality " \
5977
"not supported", command->query);
5980
if (command->abort_on_error)
5981
die("query '%s' failed: %d: %s", command->query, err_errno, err_error);
5983
DBUG_PRINT("info", ("expected_errors.count: %d",
5984
command->expected_errors.count));
5985
for (i= 0 ; (uint) i < command->expected_errors.count ; i++)
5987
if (((command->expected_errors.err[i].type == ERR_ERRNO) &&
5988
(command->expected_errors.err[i].code.errnum == err_errno)) ||
5989
((command->expected_errors.err[i].type == ERR_SQLSTATE) &&
5990
(strncmp(command->expected_errors.err[i].code.sqlstate,
5991
err_sqlstate, SQLSTATE_LENGTH) == 0)))
5993
if (!disable_result_log)
5995
if (command->expected_errors.count == 1)
5997
/* Only log error if there is one possible error */
5998
dynstr_append_mem(ds, "ERROR ", 6);
5999
replace_dynstr_append(ds, err_sqlstate);
6000
dynstr_append_mem(ds, ": ", 2);
6001
replace_dynstr_append(ds, err_error);
6002
dynstr_append_mem(ds,"\n",1);
6004
/* Don't log error if we may not get an error */
6005
else if (command->expected_errors.err[0].type == ERR_SQLSTATE ||
6006
(command->expected_errors.err[0].type == ERR_ERRNO &&
6007
command->expected_errors.err[0].code.errnum != 0))
6008
dynstr_append(ds,"Got one of the listed errors\n");
6015
DBUG_PRINT("info",("i: %d expected_errors: %d", i,
6016
command->expected_errors.count));
6018
if (!disable_result_log)
6020
dynstr_append_mem(ds, "ERROR ",6);
6021
replace_dynstr_append(ds, err_sqlstate);
6022
dynstr_append_mem(ds, ": ", 2);
6023
replace_dynstr_append(ds, err_error);
6024
dynstr_append_mem(ds, "\n", 1);
6029
if (command->expected_errors.err[0].type == ERR_ERRNO)
6030
die("query '%s' failed with wrong errno %d: '%s', instead of %d...",
6031
command->query, err_errno, err_error,
6032
command->expected_errors.err[0].code.errnum);
6034
die("query '%s' failed with wrong sqlstate %s: '%s', instead of %s...",
6035
command->query, err_sqlstate, err_error,
6036
command->expected_errors.err[0].code.sqlstate);
6044
Handle absence of errors after execution
6048
q - context of query
6051
error - function will not return
6054
void handle_no_error(struct st_command *command)
6056
DBUG_ENTER("handle_no_error");
6058
if (command->expected_errors.err[0].type == ERR_ERRNO &&
6059
command->expected_errors.err[0].code.errnum != 0)
6061
/* Error code we wanted was != 0, i.e. not an expected success */
6062
die("query '%s' succeeded - should have failed with errno %d...",
6063
command->query, command->expected_errors.err[0].code.errnum);
6065
else if (command->expected_errors.err[0].type == ERR_SQLSTATE &&
6066
strcmp(command->expected_errors.err[0].code.sqlstate,"00000") != 0)
6068
/* SQLSTATE we wanted was != "00000", i.e. not an expected success */
6069
die("query '%s' succeeded - should have failed with sqlstate %s...",
6070
command->query, command->expected_errors.err[0].code.sqlstate);
6078
Run query using prepared statement C API
6082
mysql - mysql handle
6083
command - currrent command pointer
6084
query - query string to execute
6085
query_len - length query string to execute
6086
ds - output buffer where to store result form query
6089
error - function will not return
6092
void run_query_stmt(MYSQL *mysql, struct st_command *command,
6093
char *query, int query_len, DYNAMIC_STRING *ds,
6094
DYNAMIC_STRING *ds_warnings)
6096
MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */
6098
DYNAMIC_STRING ds_prepare_warnings;
6099
DYNAMIC_STRING ds_execute_warnings;
6100
DBUG_ENTER("run_query_stmt");
6101
DBUG_PRINT("query", ("'%-.60s'", query));
6104
Init a new stmt if it's not already one created for this connection
6106
if(!(stmt= cur_con->stmt))
6108
if (!(stmt= mysql_stmt_init(mysql)))
6109
die("unable to init stmt structure");
6110
cur_con->stmt= stmt;
6113
/* Init dynamic strings for warnings */
6114
if (!disable_warnings)
6116
init_dynamic_string(&ds_prepare_warnings, NULL, 0, 256);
6117
init_dynamic_string(&ds_execute_warnings, NULL, 0, 256);
6123
if (mysql_stmt_prepare(stmt, query, query_len))
6125
handle_error(command, mysql_stmt_errno(stmt),
6126
mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
6131
Get the warnings from mysql_stmt_prepare and keep them in a
6134
if (!disable_warnings)
6135
append_warnings(&ds_prepare_warnings, mysql);
6138
No need to call mysql_stmt_bind_param() because we have no
6142
#if MYSQL_VERSION_ID >= 50000
6143
if (cursor_protocol_enabled)
6146
Use cursor when retrieving result
6148
ulong type= CURSOR_TYPE_READ_ONLY;
6149
if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type))
6150
die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s",
6151
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
6158
if (mysql_stmt_execute(stmt))
6160
handle_error(command, mysql_stmt_errno(stmt),
6161
mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
6166
When running in cursor_protocol get the warnings from execute here
6167
and keep them in a separate string for later.
6169
if (cursor_protocol_enabled && !disable_warnings)
6170
append_warnings(&ds_execute_warnings, mysql);
6173
We instruct that we want to update the "max_length" field in
6174
mysql_stmt_store_result(), this is our only way to know how much
6175
buffer to allocate for result data
6179
if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one))
6180
die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s",
6181
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
6185
If we got here the statement succeeded and was expected to do so,
6186
get data. Note that this can still give errors found during execution!
6187
Store the result of the query if if will return any fields
6189
if (mysql_stmt_field_count(stmt) && mysql_stmt_store_result(stmt))
6191
handle_error(command, mysql_stmt_errno(stmt),
6192
mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
6196
/* If we got here the statement was both executed and read successfully */
6197
handle_no_error(command);
6198
if (!disable_result_log)
6201
Not all statements creates a result set. If there is one we can
6202
now create another normal result set that contains the meta
6203
data. This set can be handled almost like any other non prepared
6204
statement result set.
6206
if ((res= mysql_stmt_result_metadata(stmt)) != NULL)
6208
/* Take the column count from meta info */
6209
MYSQL_FIELD *fields= mysql_fetch_fields(res);
6210
uint num_fields= mysql_num_fields(res);
6212
if (display_metadata)
6213
append_metadata(ds, fields, num_fields);
6215
if (!display_result_vertically)
6216
append_table_headings(ds, fields, num_fields);
6218
append_stmt_result(ds, stmt, fields, num_fields);
6220
mysql_free_result(res); /* Free normal result set with meta data */
6222
/* Clear prepare warnings */
6223
dynstr_set(&ds_prepare_warnings, NULL);
6228
This is a query without resultset
6232
if (!disable_warnings)
6234
/* Get the warnings from execute */
6236
/* Append warnings to ds - if there are any */
6237
if (append_warnings(&ds_execute_warnings, mysql) ||
6238
ds_execute_warnings.length ||
6239
ds_prepare_warnings.length ||
6240
ds_warnings->length)
6242
dynstr_append_mem(ds, "Warnings:\n", 10);
6243
if (ds_warnings->length)
6244
dynstr_append_mem(ds, ds_warnings->str,
6245
ds_warnings->length);
6246
if (ds_prepare_warnings.length)
6247
dynstr_append_mem(ds, ds_prepare_warnings.str,
6248
ds_prepare_warnings.length);
6249
if (ds_execute_warnings.length)
6250
dynstr_append_mem(ds, ds_execute_warnings.str,
6251
ds_execute_warnings.length);
6256
append_info(ds, mysql_affected_rows(mysql), mysql_info(mysql));
6261
if (!disable_warnings)
6263
dynstr_free(&ds_prepare_warnings);
6264
dynstr_free(&ds_execute_warnings);
6268
/* Close the statement if - no reconnect, need new prepare */
6269
if (mysql->reconnect)
6271
mysql_stmt_close(stmt);
6272
cur_con->stmt= NULL;
6276
We save the return code (mysql_stmt_errno(stmt)) from the last call sent
6277
to the server into the mysqltest builtin variable $mysql_errno. This
6278
variable then can be used from the test case itself.
6281
var_set_errno(mysql_stmt_errno(stmt));
6289
Create a util connection if one does not already exists
6290
and use that to run the query
6291
This is done to avoid implict commit when creating/dropping objects such
6295
int util_query(MYSQL* org_mysql, const char* query){
6298
DBUG_ENTER("util_query");
6300
if(!(mysql= cur_con->util_mysql))
6302
int opt_protocol= MYSQL_PROTOCOL_TCP;
6303
DBUG_PRINT("info", ("Creating util_mysql"));
6304
if (!(mysql= mysql_init(mysql)))
6305
die("Failed in mysql_init()");
6306
mysql_options(mysql, MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
6308
/* enable local infile, in non-binary builds often disabled by default */
6309
mysql_options(mysql, MYSQL_OPT_LOCAL_INFILE, 0);
6310
safe_connect(mysql, "util", org_mysql->host, org_mysql->user,
6311
org_mysql->passwd, org_mysql->db, org_mysql->port);
6313
cur_con->util_mysql= mysql;
6316
return mysql_query(mysql, query);
6327
command currrent command pointer
6329
flags control the phased/stages of query execution to be performed
6330
if QUERY_SEND_FLAG bit is on, the query will be sent. If QUERY_REAP_FLAG
6331
is on the result will be read - for regular query, both bits must be on
6334
void run_query(struct st_connection *cn, struct st_command *command, int flags)
6336
MYSQL *mysql= &cn->mysql;
6338
DYNAMIC_STRING *save_ds= NULL;
6339
DYNAMIC_STRING ds_result;
6340
DYNAMIC_STRING ds_sorted;
6341
DYNAMIC_STRING ds_warnings;
6342
DYNAMIC_STRING eval_query;
6345
my_bool view_created= 0, sp_created= 0;
6346
my_bool complete_query= ((flags & QUERY_SEND_FLAG) &&
6347
(flags & QUERY_REAP_FLAG));
6348
DBUG_ENTER("run_query");
6350
init_dynamic_string(&ds_warnings, NULL, 0, 256);
6352
/* Scan for warning before sending to server */
6353
scan_command_for_warnings(command);
6356
Evaluate query if this is an eval command
6358
if (command->type == Q_EVAL)
6360
init_dynamic_string(&eval_query, "", command->query_len+256, 1024);
6361
do_eval(&eval_query, command->query, command->end, FALSE);
6362
query = eval_query.str;
6363
query_len = eval_query.length;
6367
query = command->query;
6368
query_len = strlen(query);
6372
When command->require_file is set the output of _this_ query
6373
should be compared with an already existing file
6374
Create a temporary dynamic string to contain the output from
6377
if (command->require_file[0])
6379
init_dynamic_string(&ds_result, "", 1024, 1024);
6386
Log the query into the output buffer
6388
if (!disable_query_log && (flags & QUERY_SEND_FLAG))
6390
replace_dynstr_append_mem(ds, query, query_len);
6391
dynstr_append_mem(ds, delimiter, delimiter_length);
6392
dynstr_append_mem(ds, "\n", 1);
6395
if (view_protocol_enabled &&
6397
match_re(&view_re, query))
6400
Create the query as a view.
6401
Use replace since view can exist from a failed mysqltest run
6403
DYNAMIC_STRING query_str;
6404
init_dynamic_string(&query_str,
6405
"CREATE OR REPLACE VIEW mysqltest_tmp_v AS ",
6407
dynstr_append_mem(&query_str, query, query_len);
6408
if (util_query(mysql, query_str.str))
6411
Failed to create the view, this is not fatal
6412
just run the query the normal way
6414
DBUG_PRINT("view_create_error",
6415
("Failed to create view '%s': %d: %s", query_str.str,
6416
mysql_errno(mysql), mysql_error(mysql)));
6418
/* Log error to create view */
6419
verbose_msg("Failed to create view '%s' %d: %s", query_str.str,
6420
mysql_errno(mysql), mysql_error(mysql));
6425
Yes, it was possible to create this query as a view
6428
query= (char*)"SELECT * FROM mysqltest_tmp_v";
6429
query_len = strlen(query);
6432
Collect warnings from create of the view that should otherwise
6433
have been produced when the SELECT was executed
6435
append_warnings(&ds_warnings, cur_con->util_mysql);
6438
dynstr_free(&query_str);
6442
if (sp_protocol_enabled &&
6444
match_re(&sp_re, query))
6447
Create the query as a stored procedure
6448
Drop first since sp can exist from a failed mysqltest run
6450
DYNAMIC_STRING query_str;
6451
init_dynamic_string(&query_str,
6452
"DROP PROCEDURE IF EXISTS mysqltest_tmp_sp;",
6454
util_query(mysql, query_str.str);
6455
dynstr_set(&query_str, "CREATE PROCEDURE mysqltest_tmp_sp()\n");
6456
dynstr_append_mem(&query_str, query, query_len);
6457
if (util_query(mysql, query_str.str))
6460
Failed to create the stored procedure for this query,
6461
this is not fatal just run the query the normal way
6463
DBUG_PRINT("sp_create_error",
6464
("Failed to create sp '%s': %d: %s", query_str.str,
6465
mysql_errno(mysql), mysql_error(mysql)));
6467
/* Log error to create sp */
6468
verbose_msg("Failed to create sp '%s' %d: %s", query_str.str,
6469
mysql_errno(mysql), mysql_error(mysql));
6476
query= (char*)"CALL mysqltest_tmp_sp()";
6477
query_len = strlen(query);
6479
dynstr_free(&query_str);
6482
if (display_result_sorted)
6485
Collect the query output in a separate string
6486
that can be sorted before it's added to the
6487
global result string
6489
init_dynamic_string(&ds_sorted, "", 1024, 1024);
6490
save_ds= ds; /* Remember original ds */
6495
Find out how to run this query
6497
Always run with normal C API if it's not a complete
6500
If it is a '?' in the query it may be a SQL level prepared
6501
statement already and we can't do it twice
6503
if (ps_protocol_enabled &&
6505
match_re(&ps_re, query))
6506
run_query_stmt(mysql, command, query, query_len, ds, &ds_warnings);
6508
run_query_normal(cn, command, flags, query, query_len,
6511
if (display_result_sorted)
6513
/* Sort the result set and append it to result */
6514
dynstr_append_sorted(save_ds, &ds_sorted);
6516
dynstr_free(&ds_sorted);
6521
if (util_query(mysql, "DROP PROCEDURE mysqltest_tmp_sp "))
6522
die("Failed to drop sp: %d: %s", mysql_errno(mysql), mysql_error(mysql));
6527
if (util_query(mysql, "DROP VIEW mysqltest_tmp_v "))
6528
die("Failed to drop view: %d: %s",
6529
mysql_errno(mysql), mysql_error(mysql));
6532
if (command->require_file[0])
6534
/* A result file was specified for _this_ query
6535
and the output should be checked against an already
6536
existing file which has been specified using --require or --result
6538
check_require(ds, command->require_file);
6541
dynstr_free(&ds_warnings);
6542
if (ds == &ds_result)
6543
dynstr_free(&ds_result);
6544
if (command->type == Q_EVAL)
6545
dynstr_free(&eval_query);
6549
/****************************************************************************/
6551
Functions to detect different SQL statements
6554
char *re_eprint(int err)
6556
static char epbuf[100];
6557
size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL,
6558
epbuf, sizeof(epbuf));
6559
assert(len <= sizeof(epbuf));
6563
void init_re_comp(my_regex_t *re, const char* str)
6565
int err= my_regcomp(re, str, (REG_EXTENDED | REG_ICASE | REG_NOSUB),
6566
&my_charset_latin1);
6570
int len= my_regerror(err, re, erbuf, sizeof(erbuf));
6571
die("error %s, %d/%d `%s'\n",
6572
re_eprint(err), len, (int)sizeof(erbuf), erbuf);
6579
Filter for queries that can be run using the
6580
MySQL Prepared Statements C API
6582
const char *ps_re_str =
6584
"[[:space:]]*REPLACE[[:space:]]|"
6585
"[[:space:]]*INSERT[[:space:]]|"
6586
"[[:space:]]*UPDATE[[:space:]]|"
6587
"[[:space:]]*DELETE[[:space:]]|"
6588
"[[:space:]]*SELECT[[:space:]]|"
6589
"[[:space:]]*CREATE[[:space:]]+TABLE[[:space:]]|"
6590
"[[:space:]]*DO[[:space:]]|"
6591
"[[:space:]]*SET[[:space:]]+OPTION[[:space:]]|"
6592
"[[:space:]]*DELETE[[:space:]]+MULTI[[:space:]]|"
6593
"[[:space:]]*UPDATE[[:space:]]+MULTI[[:space:]]|"
6594
"[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])";
6597
Filter for queries that can be run using the
6600
const char *sp_re_str =ps_re_str;
6603
Filter for queries that can be run as views
6605
const char *view_re_str =
6607
"[[:space:]]*SELECT[[:space:]])";
6609
init_re_comp(&ps_re, ps_re_str);
6610
init_re_comp(&sp_re, sp_re_str);
6611
init_re_comp(&view_re, view_re_str);
6615
int match_re(my_regex_t *re, char *str)
6617
int err= my_regexec(re, str, (size_t)0, NULL, 0);
6621
else if (err == REG_NOMATCH)
6626
int len= my_regerror(err, re, erbuf, sizeof(erbuf));
6627
die("error %s, %d/%d `%s'\n",
6628
re_eprint(err), len, (int)sizeof(erbuf), erbuf);
6637
my_regfree(&view_re);
6641
/****************************************************************************/
6643
void get_command_type(struct st_command* command)
6647
DBUG_ENTER("get_command_type");
6649
if (*command->query == '}')
6651
command->type = Q_END_BLOCK;
6655
save= command->query[command->first_word_len];
6656
command->query[command->first_word_len]= 0;
6657
type= find_type(command->query, &command_typelib, 1+2);
6658
command->query[command->first_word_len]= save;
6661
command->type=(enum enum_commands) type; /* Found command */
6664
Look for case where "query" was explicitly specified to
6665
force command being sent to server
6667
if (type == Q_QUERY)
6669
/* Skip the "query" part */
6670
command->query= command->first_argument;
6675
/* No mysqltest command matched */
6677
if (command->type != Q_COMMENT_WITH_COMMAND)
6679
/* A query that will sent to mysqld */
6680
command->type= Q_QUERY;
6684
/* -- comment that didn't contain a mysqltest command */
6685
command->type= Q_COMMENT;
6686
warning_msg("Suspicious command '--%s' detected, was this intentional? "\
6687
"Use # instead of -- to avoid this warning",
6690
if (command->first_word_len &&
6691
strcmp(command->query + command->first_word_len - 1, delimiter) == 0)
6694
Detect comment with command using extra delimiter
6695
Ex --disable_query_log;
6696
^ Extra delimiter causing the command
6699
save= command->query[command->first_word_len-1];
6700
command->query[command->first_word_len-1]= 0;
6701
if (find_type(command->query, &command_typelib, 1+2) > 0)
6702
die("Extra delimiter \";\" found");
6703
command->query[command->first_word_len-1]= save;
6709
/* Set expected error on command */
6710
memcpy(&command->expected_errors, &saved_expected_errors,
6711
sizeof(saved_expected_errors));
6712
DBUG_PRINT("info", ("There are %d expected errors",
6713
command->expected_errors.count));
6714
command->abort_on_error= (command->expected_errors.count == 0 &&
6723
Record how many milliseconds it took to execute the test file
6724
up until the current line and save it in the dynamic string ds_progress.
6726
The ds_progress will be dumped to <test_name>.progress when
6731
void mark_progress(struct st_command* command __attribute__((unused)),
6735
ulonglong timer= timer_now();
6736
if (!progress_start)
6737
progress_start= timer;
6738
timer-= progress_start;
6740
/* Milliseconds since start */
6741
end= longlong2str(timer, buf, 10);
6742
dynstr_append_mem(&ds_progress, buf, (int)(end-buf));
6743
dynstr_append_mem(&ds_progress, "\t", 1);
6745
/* Parser line number */
6746
end= int10_to_str(line, buf, 10);
6747
dynstr_append_mem(&ds_progress, buf, (int)(end-buf));
6748
dynstr_append_mem(&ds_progress, "\t", 1);
6751
dynstr_append(&ds_progress, cur_file->file_name);
6752
dynstr_append_mem(&ds_progress, ":", 1);
6755
end= int10_to_str(cur_file->lineno, buf, 10);
6756
dynstr_append_mem(&ds_progress, buf, (int)(end-buf));
6759
dynstr_append_mem(&ds_progress, "\n", 1);
6764
int main(int argc, char **argv)
6766
struct st_command *command;
6767
my_bool q_send_flag= 0, abort_flag= 0;
6768
uint command_executed= 0, last_command_executed= 0;
6769
char save_file[FN_REFLEN];
6776
/* Init expected errors */
6777
memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
6779
/* Init connections */
6780
memset(connections, 0, sizeof(connections));
6781
connections_end= connections +
6782
(sizeof(connections)/sizeof(struct st_connection)) - 1;
6783
next_con= connections + 1;
6785
#ifdef EMBEDDED_LIBRARY
6786
/* set appropriate stack for the 'query' threads */
6787
(void) pthread_attr_init(&cn_thd_attrib);
6788
pthread_attr_setstacksize(&cn_thd_attrib, DEFAULT_THREAD_STACK);
6789
#endif /*EMBEDDED_LIBRARY*/
6791
/* Init file stack */
6792
memset(file_stack, 0, sizeof(file_stack));
6794
file_stack + (sizeof(file_stack)/sizeof(struct st_test_file)) - 1;
6795
cur_file= file_stack;
6797
/* Init block stack */
6798
memset(block_stack, 0, sizeof(block_stack));
6800
block_stack + (sizeof(block_stack)/sizeof(struct st_block)) - 1;
6801
cur_block= block_stack;
6802
cur_block->ok= TRUE; /* Outer block should always be executed */
6803
cur_block->cmd= cmd_none;
6805
my_init_dynamic_array(&q_lines, sizeof(struct st_command*), 1024, 1024);
6807
if (hash_init(&var_hash, charset_info,
6808
1024, 0, 0, get_var_key, var_free, MYF(0)))
6809
die("Variable hash initialization failed");
6811
var_set_string("$MYSQL_SERVER_VERSION", MYSQL_SERVER_VERSION);
6813
memset(&master_pos, 0, sizeof(master_pos));
6815
parser.current_line= parser.read_lines= 0;
6816
memset(&var_reg, 0, sizeof(var_reg));
6818
init_builtin_echo();
6824
init_win_path_patterns();
6827
init_dynamic_string(&ds_res, "", 65536, 65536);
6828
init_dynamic_string(&ds_progress, "", 0, 2048);
6829
init_dynamic_string(&ds_warning_messages, "", 0, 2048);
6830
parse_args(argc, argv);
6832
var_set_int("$PS_PROTOCOL", ps_protocol);
6833
var_set_int("$SP_PROTOCOL", sp_protocol);
6834
var_set_int("$VIEW_PROTOCOL", view_protocol);
6835
var_set_int("$CURSOR_PROTOCOL", cursor_protocol);
6837
DBUG_PRINT("info",("result_file: '%s'",
6838
result_file_name ? result_file_name : ""));
6839
if (mysql_server_init(embedded_server_arg_count,
6840
embedded_server_args,
6841
(char**) embedded_server_groups))
6842
die("Can't initialize MySQL server");
6843
server_initialized= 1;
6844
if (cur_file == file_stack && cur_file->file == 0)
6846
cur_file->file= stdin;
6847
cur_file->file_name= my_strdup("<stdin>", MYF(MY_WME));
6848
cur_file->lineno= 1;
6851
ps_protocol_enabled= ps_protocol;
6852
sp_protocol_enabled= sp_protocol;
6853
view_protocol_enabled= view_protocol;
6854
cursor_protocol_enabled= cursor_protocol;
6855
/* Cursor protcol implies ps protocol */
6856
if (cursor_protocol_enabled)
6857
ps_protocol_enabled= 1;
6859
cur_con= connections;
6860
if (!( mysql_init(&cur_con->mysql)))
6861
die("Failed in mysql_init()");
6863
mysql_options(&cur_con->mysql,MYSQL_OPT_COMPRESS,NullS);
6864
mysql_options(&cur_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
6865
mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME,
6866
charset_info->csname);
6867
int opt_protocol= MYSQL_PROTOCOL_TCP;
6868
mysql_options(&cur_con->mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
6869
if (opt_charsets_dir)
6870
mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_DIR,
6873
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
6877
mysql_ssl_set(&cur_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
6878
opt_ssl_capath, opt_ssl_cipher);
6879
#if MYSQL_VERSION_ID >= 50000
6880
/* Turn on ssl_verify_server_cert only if host is "localhost" */
6881
opt_ssl_verify_server_cert= opt_host && !strcmp(opt_host, "localhost");
6882
mysql_options(&cur_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
6883
&opt_ssl_verify_server_cert);
6888
if (!(cur_con->name = my_strdup("default", MYF(MY_WME))))
6889
die("Out of memory");
6891
safe_connect(&cur_con->mysql, cur_con->name, opt_host, opt_user, opt_pass,
6894
/* Use all time until exit if no explicit 'start_timer' */
6895
timer_start= timer_now();
6898
Initialize $mysql_errno with -1, so we can
6899
- distinguish it from valid values ( >= 0 ) and
6900
- detect if there was never a command sent to the server
6904
/* Update $mysql_get_server_version to that of current connection */
6905
var_set_mysql_get_server_version(&cur_con->mysql);
6909
open_file(opt_include);
6912
while (!read_command(&command) && !abort_flag)
6914
int current_line_inc = 1, processed = 0;
6915
if (command->type == Q_UNKNOWN || command->type == Q_COMMENT_WITH_COMMAND)
6916
get_command_type(command);
6918
if (parsing_disabled &&
6919
command->type != Q_ENABLE_PARSING &&
6920
command->type != Q_DISABLE_PARSING)
6922
command->type= Q_COMMENT;
6923
scan_command_for_warnings(command);
6928
command->last_argument= command->first_argument;
6930
switch (command->type) {
6932
do_connect(command);
6934
case Q_CONNECTION: select_connection(command); break;
6937
do_close_connection(command); break;
6938
case Q_ENABLE_QUERY_LOG: disable_query_log=0; break;
6939
case Q_DISABLE_QUERY_LOG: disable_query_log=1; break;
6940
case Q_ENABLE_ABORT_ON_ERROR: abort_on_error=1; break;
6941
case Q_DISABLE_ABORT_ON_ERROR: abort_on_error=0; break;
6942
case Q_ENABLE_RESULT_LOG: disable_result_log=0; break;
6943
case Q_DISABLE_RESULT_LOG: disable_result_log=1; break;
6944
case Q_ENABLE_WARNINGS: disable_warnings=0; break;
6945
case Q_DISABLE_WARNINGS: disable_warnings=1; break;
6946
case Q_ENABLE_INFO: disable_info=0; break;
6947
case Q_DISABLE_INFO: disable_info=1; break;
6948
case Q_ENABLE_METADATA: display_metadata=1; break;
6949
case Q_DISABLE_METADATA: display_metadata=0; break;
6950
case Q_SOURCE: do_source(command); break;
6951
case Q_SLEEP: do_sleep(command, 0); break;
6952
case Q_REAL_SLEEP: do_sleep(command, 1); break;
6953
case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(command); break;
6954
case Q_INC: do_modify_var(command, DO_INC); break;
6955
case Q_DEC: do_modify_var(command, DO_DEC); break;
6956
case Q_ECHO: do_echo(command); command_executed++; break;
6957
case Q_SYSTEM: do_system(command); break;
6958
case Q_REMOVE_FILE: do_remove_file(command); break;
6959
case Q_MKDIR: do_mkdir(command); break;
6960
case Q_RMDIR: do_rmdir(command); break;
6961
case Q_FILE_EXIST: do_file_exist(command); break;
6962
case Q_WRITE_FILE: do_write_file(command); break;
6963
case Q_APPEND_FILE: do_append_file(command); break;
6964
case Q_DIFF_FILES: do_diff_files(command); break;
6965
case Q_SEND_QUIT: do_send_quit(command); break;
6966
case Q_CHANGE_USER: do_change_user(command); break;
6967
case Q_CAT_FILE: do_cat_file(command); break;
6968
case Q_COPY_FILE: do_copy_file(command); break;
6969
case Q_CHMOD_FILE: do_chmod_file(command); break;
6970
case Q_PERL: do_perl(command); break;
6972
do_delimiter(command);
6974
case Q_DISPLAY_VERTICAL_RESULTS:
6975
display_result_vertically= TRUE;
6977
case Q_DISPLAY_HORIZONTAL_RESULTS:
6978
display_result_vertically= FALSE;
6980
case Q_SORTED_RESULT:
6982
Turn on sorting of result set, will be reset after next
6985
display_result_sorted= TRUE;
6987
case Q_LET: do_let(command); break;
6989
die("'eval_result' command is deprecated");
6991
case Q_QUERY_VERTICAL:
6992
case Q_QUERY_HORIZONTAL:
6993
if (command->query == command->query_buf)
6995
/* Skip the first part of command, i.e query_xxx */
6996
command->query= command->first_argument;
6997
command->first_word_len= 0;
7003
my_bool old_display_result_vertically= display_result_vertically;
7004
/* Default is full query, both reap and send */
7005
int flags= QUERY_REAP_FLAG | QUERY_SEND_FLAG;
7009
/* Last command was an empty 'send' */
7010
flags= QUERY_SEND_FLAG;
7013
else if (command->type == Q_REAP)
7015
flags= QUERY_REAP_FLAG;
7018
/* Check for special property for this query */
7019
display_result_vertically|= (command->type == Q_QUERY_VERTICAL);
7023
strmake(command->require_file, save_file, sizeof(save_file) - 1);
7026
run_query(cur_con, command, flags);
7028
command->last_argument= command->end;
7030
/* Restore settings */
7031
display_result_vertically= old_display_result_vertically;
7036
if (!*command->first_argument)
7039
This is a send without arguments, it indicates that _next_ query
7046
/* Remove "send" if this is first iteration */
7047
if (command->query == command->query_buf)
7048
command->query= command->first_argument;
7051
run_query() can execute a query partially, depending on the flags.
7052
QUERY_SEND_FLAG flag without QUERY_REAP_FLAG tells it to just send
7053
the query and read the result some time later when reap instruction
7054
is given on this connection.
7056
run_query(cur_con, command, QUERY_SEND_FLAG);
7058
command->last_argument= command->end;
7061
do_get_file_name(command, save_file, sizeof(save_file));
7064
do_get_errcodes(command);
7067
do_get_replace(command);
7069
case Q_REPLACE_REGEX:
7070
do_get_replace_regex(command);
7072
case Q_REPLACE_COLUMN:
7073
do_get_replace_column(command);
7075
case Q_SAVE_MASTER_POS: do_save_master_pos(); break;
7076
case Q_SYNC_WITH_MASTER: do_sync_with_master(command); break;
7077
case Q_SYNC_SLAVE_WITH_MASTER:
7079
do_save_master_pos();
7080
if (*command->first_argument)
7081
select_connection(command);
7083
select_connection_name("slave");
7084
do_sync_with_master2(0);
7087
case Q_COMMENT: /* Ignore row */
7088
command->last_argument= command->end;
7091
(void) mysql_ping(&cur_con->mysql);
7098
/* Overwrite possible earlier start of timer */
7099
timer_start= timer_now();
7102
/* End timer before ending mysqltest */
7105
case Q_CHARACTER_SET:
7106
do_set_charset(command);
7108
case Q_DISABLE_PS_PROTOCOL:
7109
ps_protocol_enabled= 0;
7110
/* Close any open statements */
7113
case Q_ENABLE_PS_PROTOCOL:
7114
ps_protocol_enabled= ps_protocol;
7116
case Q_DISABLE_RECONNECT:
7117
set_reconnect(&cur_con->mysql, 0);
7119
case Q_ENABLE_RECONNECT:
7120
set_reconnect(&cur_con->mysql, 1);
7121
/* Close any open statements - no reconnect, need new prepare */
7124
case Q_DISABLE_PARSING:
7125
if (parsing_disabled == 0)
7126
parsing_disabled= 1;
7128
die("Parsing is already disabled");
7130
case Q_ENABLE_PARSING:
7132
Ensure we don't get parsing_disabled < 0 as this would accidentally
7133
disable code we don't want to have disabled
7135
if (parsing_disabled == 1)
7136
parsing_disabled= 0;
7138
die("Parsing is already enabled");
7141
/* Abort test with error code and error message */
7142
die("%s", command->first_argument);
7145
/* Stop processing any more commands */
7149
abort_not_supported_test("%s", command->first_argument);
7153
die("result, deprecated command");
7164
current_line_inc= 0;
7165
switch (command->type) {
7166
case Q_WHILE: do_block(cmd_while, command); break;
7167
case Q_IF: do_block(cmd_if, command); break;
7168
case Q_END_BLOCK: do_done(command); break;
7169
default: current_line_inc = 1; break;
7173
check_eol_junk(command->last_argument);
7175
if (command->type != Q_ERROR &&
7176
command->type != Q_COMMENT)
7179
As soon as any non "error" command or comment has been executed,
7180
the array with expected errors should be cleared
7182
memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
7185
if (command_executed != last_command_executed)
7188
As soon as any command has been executed,
7189
the replace structures should be cleared
7193
/* Also reset "sorted_result" */
7194
display_result_sorted= FALSE;
7196
last_command_executed= command_executed;
7198
parser.current_line += current_line_inc;
7199
if ( opt_mark_progress )
7200
mark_progress(command, parser.current_line);
7205
if (parsing_disabled)
7206
die("Test ended with parsing disabled");
7209
The whole test has been executed _sucessfully_.
7210
Time to compare result or save it to record file.
7211
The entire output from test is now kept in ds_res.
7215
if (result_file_name)
7217
/* A result file has been specified */
7221
/* Recording - dump the output from test to result file */
7222
str_to_file(result_file_name, ds_res.str, ds_res.length);
7226
/* Check that the output from test is equal to result file
7227
- detect missing result file
7228
- detect zero size result file
7230
check_result(&ds_res);
7235
/* No result_file_name specified to compare with, print to stdout */
7236
printf("%s", ds_res.str);
7241
die("The test didn't produce any output");
7244
if (!command_executed &&
7245
result_file_name && my_stat(result_file_name, &res_info, 0))
7248
my_stat() successful on result file. Check if we have not run a
7249
single query, but we do have a result file that contains data.
7250
Note that we don't care, if my_stat() fails. For example, for a
7251
non-existing or non-readable file, we assume it's fine to have
7252
no query output from the test file, e.g. regarded as no error.
7254
die("No queries executed but result file found!");
7257
if ( opt_mark_progress && result_file_name )
7260
/* Dump warning messages */
7261
if (result_file_name && ds_warning_messages.length)
7262
dump_warning_messages();
7265
/* Yes, if we got this far the test has suceeded! Sakila smiles */
7266
cleanup_and_exit(0);
7267
return 0; /* Keep compiler happy too */
7272
A primitive timer that give results in milliseconds if the
7273
--timer-file=<filename> is given. The timer result is written
7274
to that file when the result is available. To not confuse
7275
mysql-test-run with an old obsolete result, we remove the file
7276
before executing any commands. The time we measure is
7278
- If no explicit 'start_timer' or 'end_timer' is given in the
7279
test case, the timer measure how long we execute in mysqltest.
7281
- If only 'start_timer' is given we measure how long we execute
7282
from that point until we terminate mysqltest.
7284
- If only 'end_timer' is given we measure how long we execute
7285
from that we enter mysqltest to the 'end_timer' is command is
7288
- If both 'start_timer' and 'end_timer' are given we measure
7289
the time between executing the two commands.
7292
void timer_output(void)
7297
ulonglong timer= timer_now() - timer_start;
7298
end= longlong2str(timer, buf, 10);
7299
str_to_file(timer_file,buf, (int) (end-buf));
7300
/* Timer has been written to the file, don't use it anymore */
7306
ulonglong timer_now(void)
7308
return my_micro_time() / 1000;
7313
Get arguments for replace_columns. The syntax is:
7314
replace-column column_number to_string [column_number to_string ...]
7315
Where each argument may be quoted with ' or "
7316
A argument may also be a variable, in which case the value of the
7317
variable is replaced.
7320
void do_get_replace_column(struct st_command *command)
7322
char *from= command->first_argument;
7324
DBUG_ENTER("get_replace_columns");
7326
free_replace_column();
7328
die("Missing argument in %s", command->query);
7330
/* Allocate a buffer for results */
7331
start= buff= my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
7337
to= get_string(&buff, &from, command);
7338
if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS)
7339
die("Wrong column number to replace_column in '%s'", command->query);
7341
die("Wrong number of arguments to replace_column in '%s'", command->query);
7342
to= get_string(&buff, &from, command);
7343
my_free(replace_column[column_number-1], MY_ALLOW_ZERO_PTR);
7344
replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE));
7345
set_if_bigger(max_replace_column, column_number);
7347
my_free(start, MYF(0));
7348
command->last_argument= command->end;
7352
void free_replace_column()
7355
for (i=0 ; i < max_replace_column ; i++)
7357
if (replace_column[i])
7359
my_free(replace_column[i], 0);
7360
replace_column[i]= 0;
7363
max_replace_column= 0;
7367
/****************************************************************************/
7372
/* Definitions for replace result */
7374
typedef struct st_pointer_array { /* when using array-strings */
7375
TYPELIB typelib; /* Pointer to strings */
7376
uchar *str; /* Strings is here */
7377
int7 *flag; /* Flag about each var. */
7378
uint array_allocs,max_count,length,max_length;
7382
struct st_replace *init_replace(char * *from, char * *to, uint count,
7383
char * word_end_chars);
7384
int insert_pointer_name(POINTER_ARRAY *pa,char * name);
7385
void replace_strings_append(struct st_replace *rep, DYNAMIC_STRING* ds,
7386
const char *from, int len);
7387
void free_pointer_array(POINTER_ARRAY *pa);
7389
struct st_replace *glob_replace;
7392
Get arguments for replace. The syntax is:
7393
replace from to [from to ...]
7394
Where each argument may be quoted with ' or "
7395
A argument may also be a variable, in which case the value of the
7396
variable is replaced.
7399
void do_get_replace(struct st_command *command)
7402
char *from= command->first_argument;
7404
char word_end_chars[256], *pos;
7405
POINTER_ARRAY to_array, from_array;
7406
DBUG_ENTER("get_replace");
7410
bzero((char*) &to_array,sizeof(to_array));
7411
bzero((char*) &from_array,sizeof(from_array));
7413
die("Missing argument in %s", command->query);
7414
start= buff= my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
7418
to= get_string(&buff, &from, command);
7420
die("Wrong number of arguments to replace_result in '%s'",
7422
insert_pointer_name(&from_array,to);
7423
to= get_string(&buff, &from, command);
7424
insert_pointer_name(&to_array,to);
7426
for (i= 1,pos= word_end_chars ; i < 256 ; i++)
7427
if (my_isspace(charset_info,i))
7429
*pos=0; /* End pointer */
7430
if (!(glob_replace= init_replace((char**) from_array.typelib.type_names,
7431
(char**) to_array.typelib.type_names,
7432
(uint) from_array.typelib.count,
7434
die("Can't initialize replace from '%s'", command->query);
7435
free_pointer_array(&from_array);
7436
free_pointer_array(&to_array);
7437
my_free(start, MYF(0));
7438
command->last_argument= command->end;
7445
DBUG_ENTER("free_replace");
7448
my_free(glob_replace,MYF(0));
7455
typedef struct st_replace {
7457
struct st_replace *next[256];
7460
typedef struct st_replace_found {
7462
char *replace_string;
7468
void replace_strings_append(REPLACE *rep, DYNAMIC_STRING* ds,
7470
int len __attribute__((unused)))
7472
register REPLACE *rep_pos;
7473
register REPLACE_STRING *rep_str;
7474
const char *start, *from;
7475
DBUG_ENTER("replace_strings_append");
7481
/* Loop through states */
7482
DBUG_PRINT("info", ("Looping through states"));
7483
while (!rep_pos->found)
7484
rep_pos= rep_pos->next[(uchar) *from++];
7486
/* Does this state contain a string to be replaced */
7487
if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string)
7489
/* No match found */
7490
dynstr_append_mem(ds, start, from - start - 1);
7491
DBUG_PRINT("exit", ("Found no more string to replace, appended: %s", start));
7495
/* Found a string that needs to be replaced */
7496
DBUG_PRINT("info", ("found: %d, to_offset: %d, from_offset: %d, string: %s",
7497
rep_str->found, rep_str->to_offset,
7498
rep_str->from_offset, rep_str->replace_string));
7500
/* Append part of original string before replace string */
7501
dynstr_append_mem(ds, start, (from - rep_str->to_offset) - start);
7503
/* Append replace string */
7504
dynstr_append_mem(ds, rep_str->replace_string,
7505
strlen(rep_str->replace_string));
7507
if (!*(from-=rep_str->from_offset) && rep_pos->found != 2)
7509
/* End of from string */
7510
DBUG_PRINT("exit", ("Found end of from string"));
7513
DBUG_ASSERT(from <= str+len);
7521
Regex replace functions
7525
/* Stores regex substitutions */
7529
char* pattern; /* Pattern to be replaced */
7530
char* replace; /* String or expression to replace the pattern with */
7531
int icase; /* true if the match is case insensitive */
7534
struct st_replace_regex
7536
DYNAMIC_ARRAY regex_arr; /* stores a list of st_regex subsitutions */
7539
Temporary storage areas for substitutions. To reduce unnessary copying
7540
and memory freeing/allocation, we pre-allocate two buffers, and alternate
7541
their use, one for input/one for output, the roles changing on the next
7542
st_regex substition. At the end of substitutions buf points to the
7543
one containing the final result.
7552
struct st_replace_regex *glob_replace_regex= 0;
7554
int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace,
7555
char *string, int icase);
7560
Finds the next (non-escaped) '/' in the expression.
7561
(If the character '/' is needed, it can be escaped using '\'.)
7564
#define PARSE_REGEX_ARG \
7565
while (p < expr_end) \
7570
if (last_c == '\\') \
7588
Initializes the regular substitution expression to be used in the
7589
result output of test.
7591
Returns: st_replace_regex struct with pairs of substitutions
7594
struct st_replace_regex* init_replace_regex(char* expr)
7596
struct st_replace_regex* res;
7597
char* buf,*expr_end;
7600
uint expr_len= strlen(expr);
7602
struct st_regex reg;
7604
/* my_malloc() will die on fail with MY_FAE */
7605
res=(struct st_replace_regex*)my_malloc(
7606
sizeof(*res)+expr_len ,MYF(MY_FAE+MY_WME));
7607
my_init_dynamic_array(&res->regex_arr,sizeof(struct st_regex),128,128);
7609
buf= (char*)res + sizeof(*res);
7610
expr_end= expr + expr_len;
7614
/* for each regexp substitution statement */
7615
while (p < expr_end)
7617
bzero(®,sizeof(reg));
7618
/* find the start of the statement */
7619
while (p < expr_end)
7626
if (p == expr_end || ++p == expr_end)
7628
if (res->regex_arr.elements)
7633
/* we found the start */
7636
/* Find first argument -- pattern string to be removed */
7639
if (p == expr_end || ++p == expr_end)
7642
/* buf_p now points to the replacement pattern terminated with \0 */
7645
/* Find second argument -- replace string to replace pattern */
7651
/* skip the ending '/' in the statement */
7654
/* Check if we should do matching case insensitive */
7655
if (p < expr_end && *p == 'i')
7658
/* done parsing the statement, now place it in regex_arr */
7659
if (insert_dynamic(&res->regex_arr,(uchar*) ®))
7660
die("Out of memory");
7662
res->odd_buf_len= res->even_buf_len= 8192;
7663
res->even_buf= (char*)my_malloc(res->even_buf_len,MYF(MY_WME+MY_FAE));
7664
res->odd_buf= (char*)my_malloc(res->odd_buf_len,MYF(MY_WME+MY_FAE));
7665
res->buf= res->even_buf;
7671
die("Error parsing replace_regex \"%s\"", expr);
7676
Execute all substitutions on val.
7678
Returns: true if substituition was made, false otherwise
7679
Side-effect: Sets r->buf to be the buffer with all substitutions done.
7682
struct st_replace_regex* r
7685
struct st_replace_regex* r
7686
r->buf points at the resulting buffer
7687
r->even_buf and r->odd_buf might have been reallocated
7688
r->even_buf_len and r->odd_buf_len might have been changed
7690
TODO: at some point figure out if there is a way to do everything
7694
int multi_reg_replace(struct st_replace_regex* r,char* val)
7697
char* in_buf, *out_buf;
7701
out_buf= r->even_buf;
7702
buf_len_p= &r->even_buf_len;
7705
/* For each substitution, do the replace */
7706
for (i= 0; i < r->regex_arr.elements; i++)
7709
char* save_out_buf= out_buf;
7711
get_dynamic(&r->regex_arr,(uchar*)&re,i);
7713
if (!reg_replace(&out_buf, buf_len_p, re.pattern, re.replace,
7716
/* if the buffer has been reallocated, make adjustements */
7717
if (save_out_buf != out_buf)
7719
if (save_out_buf == r->even_buf)
7720
r->even_buf= out_buf;
7722
r->odd_buf= out_buf;
7729
swap_variables(char*,in_buf,out_buf);
7731
buf_len_p= (out_buf == r->even_buf) ? &r->even_buf_len :
7736
return (r->buf == 0);
7740
Parse the regular expression to be used in all result files
7743
The syntax is --replace_regex /from/to/i /from/to/i ...
7744
i means case-insensitive match. If omitted, the match is
7748
void do_get_replace_regex(struct st_command *command)
7750
char *expr= command->first_argument;
7751
free_replace_regex();
7752
if (!(glob_replace_regex=init_replace_regex(expr)))
7753
die("Could not init replace_regex");
7754
command->last_argument= command->end;
7757
void free_replace_regex()
7759
if (glob_replace_regex)
7761
delete_dynamic(&glob_replace_regex->regex_arr);
7762
my_free(glob_replace_regex->even_buf,MYF(MY_ALLOW_ZERO_PTR));
7763
my_free(glob_replace_regex->odd_buf,MYF(MY_ALLOW_ZERO_PTR));
7764
my_free(glob_replace_regex,MYF(0));
7765
glob_replace_regex=0;
7772
auxiluary macro used by reg_replace
7773
makes sure the result buffer has sufficient length
7775
#define SECURE_REG_BUF if (buf_len < need_buf_len) \
7777
int off= res_p - buf; \
7778
buf= (char*)my_realloc(buf,need_buf_len,MYF(MY_WME+MY_FAE)); \
7780
buf_len= need_buf_len; \
7784
Performs a regex substitution
7788
buf_p - result buffer pointer. Will change if reallocated
7789
buf_len_p - result buffer length. Will change if the buffer is reallocated
7790
pattern - regexp pattern to match
7791
replace - replacement expression
7792
string - the string to perform substituions in
7793
icase - flag, if set to 1 the match is case insensitive
7795
int reg_replace(char** buf_p, int* buf_len_p, char *pattern,
7796
char *replace, char *string, int icase)
7799
my_regmatch_t *subs;
7803
int buf_len, need_buf_len;
7804
int cflags= REG_EXTENDED;
7806
char *res_p,*str_p,*str_end;
7808
buf_len= *buf_len_p;
7809
len= strlen(string);
7810
str_end= string + len;
7812
/* start with a buffer of a reasonable size that hopefully will not
7813
need to be reallocated
7815
need_buf_len= len * 2 + 1;
7823
if ((err_code= my_regcomp(&r,pattern,cflags,&my_charset_latin1)))
7825
check_regerr(&r,err_code);
7829
subs= (my_regmatch_t*)my_malloc(sizeof(my_regmatch_t) * (r.re_nsub+1),
7830
MYF(MY_WME+MY_FAE));
7834
replace_end= replace + strlen(replace);
7836
/* for each pattern match instance perform a replacement */
7839
/* find the match */
7840
err_code= my_regexec(&r,str_p, r.re_nsub+1, subs,
7841
(str_p == string) ? REG_NOTBOL : 0);
7843
/* if regular expression error (eg. bad syntax, or out of memory) */
7844
if (err_code && err_code != REG_NOMATCH)
7846
check_regerr(&r,err_code);
7851
/* if match found */
7854
char* expr_p= replace;
7858
we need at least what we have so far in the buffer + the part
7861
need_buf_len= (res_p - buf) + (int) subs[0].rm_so;
7863
/* on this pass, calculate the memory for the result buffer */
7864
while (expr_p < replace_end)
7866
int back_ref_num= -1;
7869
if (c == '\\' && expr_p + 1 < replace_end)
7871
back_ref_num= (int) (expr_p[1] - '0');
7874
/* found a valid back_ref (eg. \1)*/
7875
if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub)
7877
regoff_t start_off, end_off;
7878
if ((start_off=subs[back_ref_num].rm_so) > -1 &&
7879
(end_off=subs[back_ref_num].rm_eo) > -1)
7881
need_buf_len += (int) (end_off - start_off);
7893
now that we know the size of the buffer,
7894
make sure it is big enough
7898
/* copy the pre-match part */
7901
memcpy(res_p, str_p, (size_t) subs[0].rm_so);
7902
res_p+= subs[0].rm_so;
7907
/* copy the match and expand back_refs */
7908
while (expr_p < replace_end)
7910
int back_ref_num= -1;
7913
if (c == '\\' && expr_p + 1 < replace_end)
7915
back_ref_num= expr_p[1] - '0';
7918
if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub)
7920
regoff_t start_off, end_off;
7921
if ((start_off=subs[back_ref_num].rm_so) > -1 &&
7922
(end_off=subs[back_ref_num].rm_eo) > -1)
7924
int block_len= (int) (end_off - start_off);
7925
memcpy(res_p,str_p + start_off, block_len);
7932
*res_p++ = *expr_p++;
7936
/* handle the post-match part */
7937
if (subs[0].rm_so == subs[0].rm_eo)
7939
if (str_p + subs[0].rm_so >= str_end)
7941
str_p += subs[0].rm_eo ;
7942
*res_p++ = *str_p++;
7946
str_p += subs[0].rm_eo;
7949
else /* no match this time, just copy the string as is */
7951
int left_in_str= str_end-str_p;
7952
need_buf_len= (res_p-buf) + left_in_str;
7954
memcpy(res_p,str_p,left_in_str);
7955
res_p += left_in_str;
7959
my_free(subs, MYF(0));
7963
*buf_len_p= buf_len;
7969
#define WORD_BIT (8*sizeof(uint))
7972
#define SET_MALLOC_HUNC 64
7973
#define LAST_CHAR_CODE 259
7975
typedef struct st_rep_set {
7976
uint *bits; /* Pointer to used sets */
7977
short next[LAST_CHAR_CODE]; /* Pointer to next sets */
7978
uint found_len; /* Best match to date */
7981
uint size_of_bits; /* For convinience */
7984
typedef struct st_rep_sets {
7985
uint count; /* Number of sets */
7986
uint extra; /* Extra sets in buffer */
7987
uint invisible; /* Sets not chown */
7989
REP_SET *set,*set_buffer;
7993
typedef struct st_found_set {
7998
typedef struct st_follow {
8005
int init_sets(REP_SETS *sets,uint states);
8006
REP_SET *make_new_set(REP_SETS *sets);
8007
void make_sets_invisible(REP_SETS *sets);
8008
void free_last_set(REP_SETS *sets);
8009
void free_sets(REP_SETS *sets);
8010
void internal_set_bit(REP_SET *set, uint bit);
8011
void internal_clear_bit(REP_SET *set, uint bit);
8012
void or_bits(REP_SET *to,REP_SET *from);
8013
void copy_bits(REP_SET *to,REP_SET *from);
8014
int cmp_bits(REP_SET *set1,REP_SET *set2);
8015
int get_next_bit(REP_SET *set,uint lastpos);
8016
int find_set(REP_SETS *sets,REP_SET *find);
8017
int find_found(FOUND_SET *found_set,uint table_offset,
8019
uint start_at_word(char * pos);
8020
uint end_of_word(char * pos);
8022
static uint found_sets=0;
8025
uint replace_len(char * str)
8030
if (str[0] == '\\' && str[1])
8038
/* Init a replace structure for further calls */
8040
REPLACE *init_replace(char * *from, char * *to,uint count,
8041
char * word_end_chars)
8043
static const int SPACE_CHAR= 256;
8044
static const int START_OF_LINE= 257;
8045
static const int END_OF_LINE= 258;
8047
uint i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr;
8048
int used_sets,chr,default_state;
8049
char used_chars[LAST_CHAR_CODE],is_word_end[256];
8050
char * pos, *to_pos, **to_array;
8052
REP_SET *set,*start_states,*word_states,*new_set;
8053
FOLLOWS *follow,*follow_ptr;
8055
FOUND_SET *found_set;
8056
REPLACE_STRING *rep_str;
8057
DBUG_ENTER("init_replace");
8059
/* Count number of states */
8060
for (i=result_len=max_length=0 , states=2 ; i < count ; i++)
8062
len=replace_len(from[i]);
8069
result_len+=(uint) strlen(to[i])+1;
8070
if (len > max_length)
8073
bzero((char*) is_word_end,sizeof(is_word_end));
8074
for (i=0 ; word_end_chars[i] ; i++)
8075
is_word_end[(uchar) word_end_chars[i]]=1;
8077
if (init_sets(&sets,states))
8080
if (!(found_set= (FOUND_SET*) my_malloc(sizeof(FOUND_SET)*max_length*count,
8086
VOID(make_new_set(&sets)); /* Set starting set */
8087
make_sets_invisible(&sets); /* Hide previus sets */
8089
word_states=make_new_set(&sets); /* Start of new word */
8090
start_states=make_new_set(&sets); /* This is first state */
8091
if (!(follow=(FOLLOWS*) my_malloc((states+2)*sizeof(FOLLOWS),MYF(MY_WME))))
8094
my_free(found_set,MYF(0));
8098
/* Init follow_ptr[] */
8099
for (i=0, states=1, follow_ptr=follow+1 ; i < count ; i++)
8101
if (from[i][0] == '\\' && from[i][1] == '^')
8103
internal_set_bit(start_states,states+1);
8106
start_states->table_offset=i;
8107
start_states->found_offset=1;
8110
else if (from[i][0] == '\\' && from[i][1] == '$')
8112
internal_set_bit(start_states,states);
8113
internal_set_bit(word_states,states);
8114
if (!from[i][2] && start_states->table_offset == (uint) ~0)
8116
start_states->table_offset=i;
8117
start_states->found_offset=0;
8122
internal_set_bit(word_states,states);
8123
if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2]))
8124
internal_set_bit(start_states,states+1);
8126
internal_set_bit(start_states,states);
8128
for (pos=from[i], len=0; *pos ; pos++)
8130
if (*pos == '\\' && *(pos+1))
8135
follow_ptr->chr = SPACE_CHAR;
8138
follow_ptr->chr = START_OF_LINE;
8141
follow_ptr->chr = END_OF_LINE;
8144
follow_ptr->chr = '\r';
8147
follow_ptr->chr = '\t';
8150
follow_ptr->chr = '\v';
8153
follow_ptr->chr = (uchar) *pos;
8158
follow_ptr->chr= (uchar) *pos;
8159
follow_ptr->table_offset=i;
8160
follow_ptr->len= ++len;
8164
follow_ptr->table_offset=i;
8165
follow_ptr->len=len;
8167
states+=(uint) len+1;
8171
for (set_nr=0,pos=0 ; set_nr < sets.count ; set_nr++)
8173
set=sets.set+set_nr;
8174
default_state= 0; /* Start from beginning */
8176
/* If end of found-string not found or start-set with current set */
8178
for (i= (uint) ~0; (i=get_next_bit(set,i)) ;)
8182
if (! default_state)
8183
default_state= find_found(found_set,set->table_offset,
8184
set->found_offset+1);
8187
copy_bits(sets.set+used_sets,set); /* Save set for changes */
8189
or_bits(sets.set+used_sets,sets.set); /* Can restart from start */
8191
/* Find all chars that follows current sets */
8192
bzero((char*) used_chars,sizeof(used_chars));
8193
for (i= (uint) ~0; (i=get_next_bit(sets.set+used_sets,i)) ;)
8195
used_chars[follow[i].chr]=1;
8196
if ((follow[i].chr == SPACE_CHAR && !follow[i+1].chr &&
8197
follow[i].len > 1) || follow[i].chr == END_OF_LINE)
8201
/* Mark word_chars used if \b is in state */
8202
if (used_chars[SPACE_CHAR])
8203
for (pos= word_end_chars ; *pos ; pos++)
8204
used_chars[(int) (uchar) *pos] = 1;
8206
/* Handle other used characters */
8207
for (chr= 0 ; chr < 256 ; chr++)
8209
if (! used_chars[chr])
8210
set->next[chr]= chr ? default_state : -1;
8213
new_set=make_new_set(&sets);
8214
set=sets.set+set_nr; /* if realloc */
8215
new_set->table_offset=set->table_offset;
8216
new_set->found_len=set->found_len;
8217
new_set->found_offset=set->found_offset+1;
8220
for (i= (uint) ~0 ; (i=get_next_bit(sets.set+used_sets,i)) ; )
8222
if (!follow[i].chr || follow[i].chr == chr ||
8223
(follow[i].chr == SPACE_CHAR &&
8224
(is_word_end[chr] ||
8225
(!chr && follow[i].len > 1 && ! follow[i+1].chr))) ||
8226
(follow[i].chr == END_OF_LINE && ! chr))
8228
if ((! chr || (follow[i].chr && !follow[i+1].chr)) &&
8229
follow[i].len > found_end)
8230
found_end=follow[i].len;
8231
if (chr && follow[i].chr)
8232
internal_set_bit(new_set,i+1); /* To next set */
8234
internal_set_bit(new_set,i);
8239
new_set->found_len=0; /* Set for testing if first */
8241
for (i= (uint) ~0; (i=get_next_bit(new_set,i)) ;)
8243
if ((follow[i].chr == SPACE_CHAR ||
8244
follow[i].chr == END_OF_LINE) && ! chr)
8248
if (follow[bit_nr-1].len < found_end ||
8249
(new_set->found_len &&
8250
(chr == 0 || !follow[bit_nr].chr)))
8251
internal_clear_bit(new_set,i);
8254
if (chr == 0 || !follow[bit_nr].chr)
8256
new_set->table_offset=follow[bit_nr].table_offset;
8257
if (chr || (follow[i].chr == SPACE_CHAR ||
8258
follow[i].chr == END_OF_LINE))
8259
new_set->found_offset=found_end; /* New match */
8260
new_set->found_len=found_end;
8267
set->next[chr] = find_found(found_set,
8268
new_set->table_offset,
8269
new_set->found_offset);
8270
free_last_set(&sets);
8273
set->next[chr] = find_set(&sets,new_set);
8276
set->next[chr] = find_set(&sets,new_set);
8281
/* Alloc replace structure for the replace-state-machine */
8283
if ((replace=(REPLACE*) my_malloc(sizeof(REPLACE)*(sets.count)+
8284
sizeof(REPLACE_STRING)*(found_sets+1)+
8285
sizeof(char *)*count+result_len,
8286
MYF(MY_WME | MY_ZEROFILL))))
8288
rep_str=(REPLACE_STRING*) (replace+sets.count);
8289
to_array= (char **) (rep_str+found_sets+1);
8290
to_pos=(char *) (to_array+count);
8291
for (i=0 ; i < count ; i++)
8294
to_pos=strmov(to_pos,to[i])+1;
8297
rep_str[0].replace_string=0;
8298
for (i=1 ; i <= found_sets ; i++)
8300
pos=from[found_set[i-1].table_offset];
8301
rep_str[i].found= !bcmp((const uchar*) pos,
8302
(const uchar*) "\\^", 3) ? 2 : 1;
8303
rep_str[i].replace_string=to_array[found_set[i-1].table_offset];
8304
rep_str[i].to_offset=found_set[i-1].found_offset-start_at_word(pos);
8305
rep_str[i].from_offset=found_set[i-1].found_offset-replace_len(pos)+
8308
for (i=0 ; i < sets.count ; i++)
8310
for (j=0 ; j < 256 ; j++)
8311
if (sets.set[i].next[j] >= 0)
8312
replace[i].next[j]=replace+sets.set[i].next[j];
8314
replace[i].next[j]=(REPLACE*) (rep_str+(-sets.set[i].next[j]-1));
8317
my_free(follow,MYF(0));
8319
my_free(found_set,MYF(0));
8320
DBUG_PRINT("exit",("Replace table has %d states",sets.count));
8321
DBUG_RETURN(replace);
8325
int init_sets(REP_SETS *sets,uint states)
8327
bzero((char*) sets,sizeof(*sets));
8328
sets->size_of_bits=((states+7)/8);
8329
if (!(sets->set_buffer=(REP_SET*) my_malloc(sizeof(REP_SET)*SET_MALLOC_HUNC,
8332
if (!(sets->bit_buffer=(uint*) my_malloc(sizeof(uint)*sets->size_of_bits*
8333
SET_MALLOC_HUNC,MYF(MY_WME))))
8335
my_free(sets->set,MYF(0));
8341
/* Make help sets invisible for nicer codeing */
8343
void make_sets_invisible(REP_SETS *sets)
8345
sets->invisible=sets->count;
8346
sets->set+=sets->count;
8350
REP_SET *make_new_set(REP_SETS *sets)
8352
uint i,count,*bit_buffer;
8357
set=sets->set+ sets->count++;
8358
bzero((char*) set->bits,sizeof(uint)*sets->size_of_bits);
8359
bzero((char*) &set->next[0],sizeof(set->next[0])*LAST_CHAR_CODE);
8360
set->found_offset=0;
8362
set->table_offset= (uint) ~0;
8363
set->size_of_bits=sets->size_of_bits;
8366
count=sets->count+sets->invisible+SET_MALLOC_HUNC;
8367
if (!(set=(REP_SET*) my_realloc((uchar*) sets->set_buffer,
8368
sizeof(REP_SET)*count,
8371
sets->set_buffer=set;
8372
sets->set=set+sets->invisible;
8373
if (!(bit_buffer=(uint*) my_realloc((uchar*) sets->bit_buffer,
8374
(sizeof(uint)*sets->size_of_bits)*count,
8377
sets->bit_buffer=bit_buffer;
8378
for (i=0 ; i < count ; i++)
8380
sets->set_buffer[i].bits=bit_buffer;
8381
bit_buffer+=sets->size_of_bits;
8383
sets->extra=SET_MALLOC_HUNC;
8384
return make_new_set(sets);
8387
void free_last_set(REP_SETS *sets)
8394
void free_sets(REP_SETS *sets)
8396
my_free(sets->set_buffer,MYF(0));
8397
my_free(sets->bit_buffer,MYF(0));
8401
void internal_set_bit(REP_SET *set, uint bit)
8403
set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
8407
void internal_clear_bit(REP_SET *set, uint bit)
8409
set->bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT));
8414
void or_bits(REP_SET *to,REP_SET *from)
8417
for (i=0 ; i < to->size_of_bits ; i++)
8418
to->bits[i]|=from->bits[i];
8422
void copy_bits(REP_SET *to,REP_SET *from)
8424
memcpy((uchar*) to->bits,(uchar*) from->bits,
8425
(size_t) (sizeof(uint) * to->size_of_bits));
8428
int cmp_bits(REP_SET *set1,REP_SET *set2)
8430
return bcmp((uchar*) set1->bits,(uchar*) set2->bits,
8431
sizeof(uint) * set1->size_of_bits);
8435
/* Get next set bit from set. */
8437
int get_next_bit(REP_SET *set,uint lastpos)
8439
uint pos,*start,*end,bits;
8441
start=set->bits+ ((lastpos+1) / WORD_BIT);
8442
end=set->bits + set->size_of_bits;
8443
bits=start[0] & ~((1 << ((lastpos+1) % WORD_BIT)) -1);
8445
while (! bits && ++start < end)
8449
pos=(uint) (start-set->bits)*WORD_BIT;
8450
while (! (bits & 1))
8458
/* find if there is a same set in sets. If there is, use it and
8459
free given set, else put in given set in sets and return its
8462
int find_set(REP_SETS *sets,REP_SET *find)
8465
for (i=0 ; i < sets->count-1 ; i++)
8467
if (!cmp_bits(sets->set+i,find))
8469
free_last_set(sets);
8473
return i; /* return new postion */
8476
/* find if there is a found_set with same table_offset & found_offset
8477
If there is return offset to it, else add new offset and return pos.
8478
Pos returned is -offset-2 in found_set_structure because it is
8479
saved in set->next and set->next[] >= 0 points to next set and
8480
set->next[] == -1 is reserved for end without replaces.
8483
int find_found(FOUND_SET *found_set,uint table_offset, int found_offset)
8486
for (i=0 ; (uint) i < found_sets ; i++)
8487
if (found_set[i].table_offset == table_offset &&
8488
found_set[i].found_offset == found_offset)
8490
found_set[i].table_offset=table_offset;
8491
found_set[i].found_offset=found_offset;
8493
return -i-2; /* return new postion */
8496
/* Return 1 if regexp starts with \b or ends with \b*/
8498
uint start_at_word(char * pos)
8500
return (((!bcmp((const uchar*) pos, (const uchar*) "\\b",2) && pos[2]) ||
8501
!bcmp((const uchar*) pos, (const uchar*) "\\^", 2)) ? 1 : 0);
8504
uint end_of_word(char * pos)
8506
char * end=strend(pos);
8507
return ((end > pos+2 && !bcmp((const uchar*) end-2,
8508
(const uchar*) "\\b", 2)) ||
8509
(end >= pos+2 && !bcmp((const uchar*) end-2,
8510
(const uchar*) "\\$",2))) ? 1 : 0;
8513
/****************************************************************************
8514
* Handle replacement of strings
8515
****************************************************************************/
8517
#define PC_MALLOC 256 /* Bytes for pointers */
8518
#define PS_MALLOC 512 /* Bytes for data */
8520
int insert_pointer_name(POINTER_ARRAY *pa,char * name)
8522
uint i,length,old_count;
8524
const char **new_array;
8525
DBUG_ENTER("insert_pointer_name");
8527
if (! pa->typelib.count)
8529
if (!(pa->typelib.type_names=(const char **)
8530
my_malloc(((PC_MALLOC-MALLOC_OVERHEAD)/
8531
(sizeof(char *)+sizeof(*pa->flag))*
8532
(sizeof(char *)+sizeof(*pa->flag))),MYF(MY_WME))))
8534
if (!(pa->str= (uchar*) my_malloc((uint) (PS_MALLOC-MALLOC_OVERHEAD),
8537
my_free((char*) pa->typelib.type_names,MYF(0));
8540
pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(uchar*)+
8542
pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
8544
pa->max_length=PS_MALLOC-MALLOC_OVERHEAD;
8547
length=(uint) strlen(name)+1;
8548
if (pa->length+length >= pa->max_length)
8550
if (!(new_pos= (uchar*) my_realloc((uchar*) pa->str,
8551
(uint) (pa->max_length+PS_MALLOC),
8554
if (new_pos != pa->str)
8556
my_ptrdiff_t diff=PTR_BYTE_DIFF(new_pos,pa->str);
8557
for (i=0 ; i < pa->typelib.count ; i++)
8558
pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff,
8562
pa->max_length+=PS_MALLOC;
8564
if (pa->typelib.count >= pa->max_count-1)
8568
len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD);
8569
if (!(new_array=(const char **) my_realloc((uchar*) pa->typelib.type_names,
8571
(sizeof(uchar*)+sizeof(*pa->flag))*
8572
(sizeof(uchar*)+sizeof(*pa->flag)),
8575
pa->typelib.type_names=new_array;
8576
old_count=pa->max_count;
8577
pa->max_count=len/(sizeof(uchar*) + sizeof(*pa->flag));
8578
pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
8579
memcpy((uchar*) pa->flag,(char *) (pa->typelib.type_names+old_count),
8580
old_count*sizeof(*pa->flag));
8582
pa->flag[pa->typelib.count]=0; /* Reset flag */
8583
pa->typelib.type_names[pa->typelib.count++]= (char*) pa->str+pa->length;
8584
pa->typelib.type_names[pa->typelib.count]= NullS; /* Put end-mark */
8585
VOID(strmov((char*) pa->str+pa->length,name));
8588
} /* insert_pointer_name */
8591
/* free pointer array */
8593
void free_pointer_array(POINTER_ARRAY *pa)
8595
if (pa->typelib.count)
8597
pa->typelib.count=0;
8598
my_free((char*) pa->typelib.type_names,MYF(0));
8599
pa->typelib.type_names=0;
8600
my_free(pa->str,MYF(0));
8602
} /* free_pointer_array */
8605
/* Functions that uses replace and replace_regex */
8607
/* Append the string to ds, with optional replace */
8608
void replace_dynstr_append_mem(DYNAMIC_STRING *ds,
8609
const char *val, int len)
8612
fix_win_paths(val, len);
8615
if (glob_replace_regex)
8618
if (!multi_reg_replace(glob_replace_regex, (char*)val))
8620
val= glob_replace_regex->buf;
8627
/* Normal replace */
8628
replace_strings_append(glob_replace, ds, val, len);
8631
dynstr_append_mem(ds, val, len);
8635
/* Append zero-terminated string to ds, with optional replace */
8636
void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val)
8638
replace_dynstr_append_mem(ds, val, strlen(val));
8641
/* Append uint to ds, with optional replace */
8642
void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val)
8644
char buff[22]; /* This should be enough for any int */
8645
char *end= longlong10_to_str(val, buff, 10);
8646
replace_dynstr_append_mem(ds, buff, end - buff);
8652
Build a list of pointer to each line in ds_input, sort
8653
the list and use the sorted list to append the strings
8654
sorted to the output ds
8657
dynstr_append_sorted
8658
ds - string where the sorted output will be appended
8659
ds_input - string to be sorted
8663
static int comp_lines(const char **a, const char **b)
8665
return (strcmp(*a,*b));
8668
void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING *ds_input)
8671
char *start= ds_input->str;
8672
DYNAMIC_ARRAY lines;
8673
DBUG_ENTER("dynstr_append_sorted");
8676
DBUG_VOID_RETURN; /* No input */
8678
my_init_dynamic_array(&lines, sizeof(const char*), 32, 32);
8680
/* First line is result header, skip past it */
8681
while (*start && *start != '\n')
8683
start++; /* Skip past \n */
8684
dynstr_append_mem(ds, ds_input->str, start - ds_input->str);
8686
/* Insert line(s) in array */
8689
char* line_end= (char*)start;
8691
/* Find end of line */
8692
while (*line_end && *line_end != '\n')
8696
/* Insert pointer to the line in array */
8697
if (insert_dynamic(&lines, (uchar*) &start))
8698
die("Out of memory inserting lines to sort");
8704
qsort(lines.buffer, lines.elements,
8705
sizeof(char**), (qsort_cmp)comp_lines);
8707
/* Create new result */
8708
for (i= 0; i < lines.elements ; i++)
8710
const char **line= dynamic_element(&lines, i, const char**);
8711
dynstr_append(ds, *line);
8712
dynstr_append(ds, "\n");
8715
delete_dynamic(&lines);