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