1 | /* Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. |
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ |
15 | |
16 | #include <my_global.h> |
17 | #include <my_sys.h> |
18 | #include <mysql.h> |
19 | #include <errmsg.h> |
20 | #include <my_compare.h> |
21 | #include <my_getopt.h> |
22 | #include <m_string.h> |
23 | #include <mysqld_error.h> |
24 | #include <mysql_version.h> |
25 | #include <sql_common.h> |
26 | #include <mysql/client_plugin.h> |
27 | |
28 | /* |
29 | If non_blocking_api_enabled is true, we will re-define all the blocking |
30 | API functions as wrappers that call the corresponding non-blocking API |
31 | and use poll()/select() to wait for them to complete. This way we can get |
32 | a good coverage testing of the non-blocking API as well. |
33 | */ |
34 | #include <my_context.h> |
35 | static my_bool non_blocking_api_enabled= 0; |
36 | #if !defined(EMBEDDED_LIBRARY) && !defined(MY_CONTEXT_DISABLE) |
37 | #define WRAP_NONBLOCK_ENABLED non_blocking_api_enabled |
38 | #include "nonblock-wrappers.h" |
39 | #endif |
40 | |
41 | #define VER "2.1" |
42 | #define MAX_TEST_QUERY_LENGTH 300 /* MAX QUERY BUFFER LENGTH */ |
43 | #define MAX_KEY MAX_INDEXES |
44 | #define MAX_SERVER_ARGS 64 |
45 | |
46 | /* set default options */ |
47 | static int opt_testcase __attribute__((unused)) = 0; |
48 | static char *opt_db= 0; |
49 | static char *opt_user= 0; |
50 | static char *opt_password= 0; |
51 | static char *opt_host= 0; |
52 | static char *opt_unix_socket= 0; |
53 | #ifdef HAVE_SMEM |
54 | static char *shared_memory_base_name= 0; |
55 | #endif |
56 | static unsigned int opt_port; |
57 | static my_bool tty_password= 0, opt_silent= 0; |
58 | |
59 | static MYSQL *mysql= 0; |
60 | static char current_db[]= "client_test_db" ; |
61 | static unsigned int test_count= 0; |
62 | static unsigned int opt_count= 0; |
63 | static unsigned int opt_count_read= 0; |
64 | static unsigned int iter_count= 0; |
65 | static my_bool have_innodb= FALSE; |
66 | static char *opt_plugin_dir= 0, *opt_default_auth= 0; |
67 | static unsigned int opt_drop_db= 1; |
68 | |
69 | static const char *opt_basedir= "./" ; |
70 | static const char *opt_vardir= "mysql-test/var" ; |
71 | static char mysql_charsets_dir[FN_REFLEN+1]; |
72 | |
73 | static longlong opt_getopt_ll_test= 0; |
74 | |
75 | static char **defaults_argv; |
76 | static int original_argc; |
77 | static char **original_argv; |
78 | static int embedded_server_arg_count= 0; |
79 | static char *embedded_server_args[MAX_SERVER_ARGS]; |
80 | |
81 | static const char *embedded_server_groups[]= { |
82 | "server" , |
83 | "embedded" , |
84 | "mysql_client_test_SERVER" , |
85 | NullS |
86 | }; |
87 | |
88 | static time_t start_time, end_time; |
89 | static double total_time; |
90 | |
91 | const char *default_dbug_option= "d:t:o,/tmp/mysql_client_test.trace" ; |
92 | |
93 | struct my_tests_st |
94 | { |
95 | const char *name; |
96 | void (*function)(); |
97 | }; |
98 | |
99 | #define (str) \ |
100 | DBUG_PRINT("test", ("name: %s", str)); \ |
101 | if (opt_silent < 2) \ |
102 | { \ |
103 | fprintf(stdout, "\n\n#####################################\n"); \ |
104 | fprintf(stdout, "%u of (%u/%u): %s", test_count++, iter_count, \ |
105 | opt_count, str); \ |
106 | fprintf(stdout, " \n#####################################\n"); \ |
107 | } |
108 | |
109 | #define (str) \ |
110 | DBUG_PRINT("test", ("name: %s", str)); \ |
111 | if (!opt_silent) \ |
112 | { \ |
113 | fprintf(stdout, "\n\n#####################################\n"); \ |
114 | fprintf(stdout, "%s", str); \ |
115 | fprintf(stdout, " \n#####################################\n"); \ |
116 | } |
117 | |
118 | static void print_error(const char *msg); |
119 | static void print_st_error(MYSQL_STMT *stmt, const char *msg); |
120 | static void client_disconnect(MYSQL* mysql); |
121 | static void get_options(int *argc, char ***argv); |
122 | |
123 | |
124 | /* |
125 | Abort unless given experssion is non-zero. |
126 | |
127 | SYNOPSIS |
128 | DIE_UNLESS(expr) |
129 | |
130 | DESCRIPTION |
131 | We can't use any kind of system assert as we need to |
132 | preserve tested invariants in release builds as well. |
133 | */ |
134 | |
135 | #define DIE_UNLESS(expr) \ |
136 | ((void) ((expr) ? 0 : (die(__FILE__, __LINE__, #expr), 0))) |
137 | #define DIE_IF(expr) \ |
138 | ((void) ((expr) ? (die(__FILE__, __LINE__, #expr), 0) : 0)) |
139 | #define DIE(expr) \ |
140 | die(__FILE__, __LINE__, #expr) |
141 | |
142 | static void die(const char *file, int line, const char *expr) |
143 | { |
144 | fflush(stdout); |
145 | fprintf(stderr, "%s:%d: check failed: '%s'\n" , file, line, expr); |
146 | fprintf(stderr, "MySQL error %d: %s\n" , mysql_errno(0), mysql_error(0)); |
147 | fflush(stderr); |
148 | exit(1); |
149 | } |
150 | |
151 | |
152 | #define myerror(msg) print_error(msg) |
153 | #define mysterror(stmt, msg) print_st_error(stmt, msg) |
154 | |
155 | #define myquery(RES) \ |
156 | { \ |
157 | int r= (RES); \ |
158 | if (r) \ |
159 | myerror(NULL); \ |
160 | DIE_UNLESS(r == 0); \ |
161 | } |
162 | |
163 | #define myquery_r(r) \ |
164 | { \ |
165 | if (r) \ |
166 | myerror(NULL); \ |
167 | DIE_UNLESS(r != 0); \ |
168 | } |
169 | |
170 | #define check_execute(stmt, r) \ |
171 | { \ |
172 | if (r) \ |
173 | mysterror(stmt, NULL); \ |
174 | DIE_UNLESS(r == 0); \ |
175 | } |
176 | |
177 | #define check_execute_r(stmt, r) \ |
178 | { \ |
179 | if (r) \ |
180 | mysterror(stmt, NULL); \ |
181 | DIE_UNLESS(r != 0); \ |
182 | } |
183 | |
184 | #define check_stmt(stmt) \ |
185 | { \ |
186 | if ( stmt == 0) \ |
187 | myerror(NULL); \ |
188 | DIE_UNLESS(stmt != 0); \ |
189 | } |
190 | |
191 | #define check_stmt_r(stmt) \ |
192 | { \ |
193 | if (stmt == 0) \ |
194 | myerror(NULL); \ |
195 | DIE_UNLESS(stmt == 0); \ |
196 | } |
197 | |
198 | #define mytest(x) if (!(x)) {myerror(NULL);DIE_UNLESS(FALSE);} |
199 | #define mytest_r(x) if ((x)) {myerror(NULL);DIE_UNLESS(FALSE);} |
200 | |
201 | |
202 | /* A workaround for Sun Forte 5.6 on Solaris x86 */ |
203 | |
204 | static int cmp_double(double *a, double *b) |
205 | { |
206 | return *a == *b; |
207 | } |
208 | |
209 | |
210 | /* Print the error message */ |
211 | |
212 | static void print_error(const char *msg) |
213 | { |
214 | if (!opt_silent) |
215 | { |
216 | if (mysql && mysql_errno(mysql)) |
217 | { |
218 | if (mysql->server_version) |
219 | fprintf(stdout, "\n [MySQL-%s]" , mysql->server_version); |
220 | else |
221 | fprintf(stdout, "\n [MySQL]" ); |
222 | fprintf(stdout, "[%d] %s\n" , mysql_errno(mysql), mysql_error(mysql)); |
223 | } |
224 | else if (msg) |
225 | fprintf(stderr, " [MySQL] %s\n" , msg); |
226 | } |
227 | } |
228 | |
229 | |
230 | static void print_st_error(MYSQL_STMT *stmt, const char *msg) |
231 | { |
232 | if (!opt_silent) |
233 | { |
234 | if (stmt && mysql_stmt_errno(stmt)) |
235 | { |
236 | if (stmt->mysql && stmt->mysql->server_version) |
237 | fprintf(stdout, "\n [MySQL-%s]" , stmt->mysql->server_version); |
238 | else |
239 | fprintf(stdout, "\n [MySQL]" ); |
240 | |
241 | fprintf(stdout, "[%d] %s\n" , mysql_stmt_errno(stmt), |
242 | mysql_stmt_error(stmt)); |
243 | } |
244 | else if (msg) |
245 | fprintf(stderr, " [MySQL] %s\n" , msg); |
246 | } |
247 | } |
248 | |
249 | /* |
250 | Enhanced version of mysql_client_init(), which may also set shared memory |
251 | base on Windows. |
252 | */ |
253 | static MYSQL *mysql_client_init(MYSQL* con) |
254 | { |
255 | MYSQL* res = mysql_init(con); |
256 | #ifdef HAVE_SMEM |
257 | if (res && shared_memory_base_name) |
258 | mysql_options(res, MYSQL_SHARED_MEMORY_BASE_NAME, shared_memory_base_name); |
259 | #endif |
260 | if (res && non_blocking_api_enabled) |
261 | mysql_options(res, MYSQL_OPT_NONBLOCK, 0); |
262 | if (opt_plugin_dir && *opt_plugin_dir) |
263 | mysql_options(res, MYSQL_PLUGIN_DIR, opt_plugin_dir); |
264 | |
265 | if (opt_default_auth && *opt_default_auth) |
266 | mysql_options(res, MYSQL_DEFAULT_AUTH, opt_default_auth); |
267 | return res; |
268 | } |
269 | |
270 | /* |
271 | Disable direct calls of mysql_init, as it disregards shared memory base. |
272 | */ |
273 | #define mysql_init(A) Please use mysql_client_init instead of mysql_init |
274 | |
275 | |
276 | /* Check if the connection has InnoDB tables */ |
277 | |
278 | static my_bool check_have_innodb(MYSQL *conn) |
279 | { |
280 | MYSQL_RES *res; |
281 | MYSQL_ROW row; |
282 | int rc; |
283 | my_bool result= FALSE; |
284 | |
285 | rc= mysql_query(conn, |
286 | "SELECT (support = 'YES' or support = 'DEFAULT' or support = 'ENABLED') " |
287 | "AS `TRUE` FROM information_schema.engines WHERE engine = 'innodb'" ); |
288 | myquery(rc); |
289 | res= mysql_use_result(conn); |
290 | DIE_UNLESS(res); |
291 | |
292 | row= mysql_fetch_row(res); |
293 | DIE_UNLESS(row); |
294 | |
295 | if (row[0] && row[1]) |
296 | result= strcmp(row[1], "1" ) == 0; |
297 | mysql_free_result(res); |
298 | return result; |
299 | } |
300 | |
301 | |
302 | /* |
303 | This is to be what mysql_query() is for mysql_real_query(), for |
304 | mysql_simple_prepare(): a variant without the 'length' parameter. |
305 | */ |
306 | |
307 | static MYSQL_STMT *STDCALL |
308 | mysql_simple_prepare(MYSQL *mysql_arg, const char *query) |
309 | { |
310 | MYSQL_STMT *stmt= mysql_stmt_init(mysql_arg); |
311 | if (stmt && mysql_stmt_prepare(stmt, query, (uint) strlen(query))) |
312 | { |
313 | mysql_stmt_close(stmt); |
314 | return 0; |
315 | } |
316 | return stmt; |
317 | } |
318 | |
319 | |
320 | /** |
321 | Connect to the server with options given by arguments to this application, |
322 | stored in global variables opt_host, opt_user, opt_password, opt_db, |
323 | opt_port and opt_unix_socket. |
324 | |
325 | @param flag[in] client_flag passed on to mysql_real_connect |
326 | @param protocol[in] MYSQL_PROTOCOL_* to use for this connection |
327 | @param auto_reconnect[in] set to 1 for auto reconnect |
328 | |
329 | @return pointer to initialized and connected MYSQL object |
330 | */ |
331 | static MYSQL* client_connect(ulong flag, uint protocol, my_bool auto_reconnect) |
332 | { |
333 | MYSQL* mysql; |
334 | int rc; |
335 | static char query[MAX_TEST_QUERY_LENGTH]; |
336 | myheader_r("client_connect" ); |
337 | |
338 | if (!opt_silent) |
339 | fprintf(stdout, "\n Establishing a connection to '%s' ..." , |
340 | opt_host ? opt_host : "" ); |
341 | |
342 | if (!(mysql= mysql_client_init(NULL))) |
343 | { |
344 | opt_silent= 0; |
345 | myerror("mysql_client_init() failed" ); |
346 | exit(1); |
347 | } |
348 | /* enable local infile, in non-binary builds often disabled by default */ |
349 | mysql_options(mysql, MYSQL_OPT_LOCAL_INFILE, 0); |
350 | mysql_options(mysql, MYSQL_OPT_PROTOCOL, &protocol); |
351 | if (opt_plugin_dir && *opt_plugin_dir) |
352 | mysql_options(mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir); |
353 | |
354 | if (opt_default_auth && *opt_default_auth) |
355 | mysql_options(mysql, MYSQL_DEFAULT_AUTH, opt_default_auth); |
356 | |
357 | if (!(mysql_real_connect(mysql, opt_host, opt_user, |
358 | opt_password, opt_db ? opt_db:"test" , opt_port, |
359 | opt_unix_socket, flag))) |
360 | { |
361 | opt_silent= 0; |
362 | myerror("connection failed" ); |
363 | mysql_close(mysql); |
364 | fprintf(stdout, "\n Check the connection options using --help or -?\n" ); |
365 | exit(1); |
366 | } |
367 | mysql_options(mysql, MYSQL_OPT_RECONNECT, &auto_reconnect); |
368 | |
369 | if (!opt_silent) |
370 | fprintf(stdout, "OK" ); |
371 | |
372 | /* set AUTOCOMMIT to ON*/ |
373 | mysql_autocommit(mysql, TRUE); |
374 | |
375 | if (!opt_silent) |
376 | { |
377 | fprintf(stdout, "\nConnected to MySQL server version: %s (%lu)\n" , |
378 | mysql_get_server_info(mysql), |
379 | (ulong) mysql_get_server_version(mysql)); |
380 | fprintf(stdout, "\n Creating a test database '%s' ..." , current_db); |
381 | } |
382 | strxmov(query, "CREATE DATABASE IF NOT EXISTS " , current_db, NullS); |
383 | |
384 | rc= mysql_query(mysql, query); |
385 | myquery(rc); |
386 | |
387 | strxmov(query, "USE " , current_db, NullS); |
388 | rc= mysql_query(mysql, query); |
389 | myquery(rc); |
390 | have_innodb= check_have_innodb(mysql); |
391 | |
392 | if (!opt_silent) |
393 | fprintf(stdout, "OK\n" ); |
394 | |
395 | return mysql; |
396 | } |
397 | |
398 | |
399 | /* Close the connection */ |
400 | |
401 | static void client_disconnect(MYSQL* mysql) |
402 | { |
403 | static char query[MAX_TEST_QUERY_LENGTH]; |
404 | |
405 | myheader_r("client_disconnect" ); |
406 | |
407 | if (mysql) |
408 | { |
409 | if (opt_drop_db) |
410 | { |
411 | if (!opt_silent) |
412 | fprintf(stdout, "\n dropping the test database '%s' ..." , current_db); |
413 | strxmov(query, "DROP DATABASE IF EXISTS " , current_db, NullS); |
414 | |
415 | mysql_query(mysql, query); |
416 | if (!opt_silent) |
417 | fprintf(stdout, "OK" ); |
418 | } |
419 | |
420 | if (!opt_silent) |
421 | fprintf(stdout, "\n closing the connection ..." ); |
422 | mysql_close(mysql); |
423 | if (!opt_silent) |
424 | fprintf(stdout, "OK\n" ); |
425 | } |
426 | } |
427 | |
428 | |
429 | /* Print dashes */ |
430 | |
431 | static void my_print_dashes(MYSQL_RES *result) |
432 | { |
433 | MYSQL_FIELD *field; |
434 | unsigned int i, j; |
435 | |
436 | mysql_field_seek(result, 0); |
437 | fputc('\t', stdout); |
438 | fputc('+', stdout); |
439 | |
440 | for(i= 0; i< mysql_num_fields(result); i++) |
441 | { |
442 | field= mysql_fetch_field(result); |
443 | for(j= 0; j < field->max_length+2; j++) |
444 | fputc('-', stdout); |
445 | fputc('+', stdout); |
446 | } |
447 | fputc('\n', stdout); |
448 | } |
449 | |
450 | |
451 | /* Print resultset metadata information */ |
452 | |
453 | static void my_print_result_metadata(MYSQL_RES *result) |
454 | { |
455 | MYSQL_FIELD *field; |
456 | unsigned int i, j; |
457 | unsigned int field_count; |
458 | |
459 | mysql_field_seek(result, 0); |
460 | if (!opt_silent) |
461 | { |
462 | fputc('\n', stdout); |
463 | fputc('\n', stdout); |
464 | } |
465 | |
466 | field_count= mysql_num_fields(result); |
467 | for(i= 0; i< field_count; i++) |
468 | { |
469 | field= mysql_fetch_field(result); |
470 | j= strlen(field->name); |
471 | if (j < field->max_length) |
472 | j= field->max_length; |
473 | if (j < 4 && !IS_NOT_NULL(field->flags)) |
474 | j= 4; |
475 | field->max_length= j; |
476 | } |
477 | if (!opt_silent) |
478 | { |
479 | my_print_dashes(result); |
480 | fputc('\t', stdout); |
481 | fputc('|', stdout); |
482 | } |
483 | |
484 | mysql_field_seek(result, 0); |
485 | for(i= 0; i< field_count; i++) |
486 | { |
487 | field= mysql_fetch_field(result); |
488 | if (!opt_silent) |
489 | fprintf(stdout, " %-*s |" , (int) field->max_length, field->name); |
490 | } |
491 | if (!opt_silent) |
492 | { |
493 | fputc('\n', stdout); |
494 | my_print_dashes(result); |
495 | } |
496 | } |
497 | |
498 | |
499 | /* Process the result set */ |
500 | |
501 | static int my_process_result_set(MYSQL_RES *result) |
502 | { |
503 | MYSQL_ROW row; |
504 | MYSQL_FIELD *field; |
505 | unsigned int i; |
506 | unsigned int row_count= 0; |
507 | |
508 | if (!result) |
509 | return 0; |
510 | |
511 | my_print_result_metadata(result); |
512 | |
513 | while ((row= mysql_fetch_row(result)) != NULL) |
514 | { |
515 | mysql_field_seek(result, 0); |
516 | if (!opt_silent) |
517 | { |
518 | fputc('\t', stdout); |
519 | fputc('|', stdout); |
520 | } |
521 | |
522 | for(i= 0; i< mysql_num_fields(result); i++) |
523 | { |
524 | field= mysql_fetch_field(result); |
525 | if (!opt_silent) |
526 | { |
527 | if (row[i] == NULL) |
528 | fprintf(stdout, " %-*s |" , (int) field->max_length, "NULL" ); |
529 | else if (IS_NUM(field->type)) |
530 | fprintf(stdout, " %*s |" , (int) field->max_length, row[i]); |
531 | else |
532 | fprintf(stdout, " %-*s |" , (int) field->max_length, row[i]); |
533 | } |
534 | } |
535 | if (!opt_silent) |
536 | { |
537 | fputc('\t', stdout); |
538 | fputc('\n', stdout); |
539 | } |
540 | row_count++; |
541 | } |
542 | if (!opt_silent) |
543 | { |
544 | if (row_count) |
545 | my_print_dashes(result); |
546 | |
547 | if (mysql_errno(mysql) != 0) |
548 | fprintf(stderr, "\n\tmysql_fetch_row() failed\n" ); |
549 | else |
550 | fprintf(stdout, "\n\t%d %s returned\n" , row_count, |
551 | row_count == 1 ? "row" : "rows" ); |
552 | } |
553 | return row_count; |
554 | } |
555 | |
556 | |
557 | static int my_process_result(MYSQL *mysql_arg) |
558 | { |
559 | MYSQL_RES *result; |
560 | int row_count; |
561 | |
562 | if (!(result= mysql_store_result(mysql_arg))) |
563 | return 0; |
564 | |
565 | row_count= my_process_result_set(result); |
566 | |
567 | mysql_free_result(result); |
568 | return row_count; |
569 | } |
570 | |
571 | |
572 | /* Process the statement result set */ |
573 | |
574 | #define MAX_RES_FIELDS 50 |
575 | #define MAX_FIELD_DATA_SIZE 255 |
576 | |
577 | static int my_process_stmt_result(MYSQL_STMT *stmt) |
578 | { |
579 | int field_count; |
580 | int row_count= 0; |
581 | MYSQL_BIND buffer[MAX_RES_FIELDS]; |
582 | MYSQL_FIELD *field; |
583 | MYSQL_RES *result; |
584 | char data[MAX_RES_FIELDS][MAX_FIELD_DATA_SIZE]; |
585 | ulong length[MAX_RES_FIELDS]; |
586 | my_bool is_null[MAX_RES_FIELDS]; |
587 | int rc, i; |
588 | |
589 | if (!(result= mysql_stmt_result_metadata(stmt))) /* No meta info */ |
590 | { |
591 | while (!mysql_stmt_fetch(stmt)) |
592 | row_count++; |
593 | return row_count; |
594 | } |
595 | |
596 | field_count= MY_MIN(mysql_num_fields(result), MAX_RES_FIELDS); |
597 | |
598 | bzero((char*) buffer, sizeof(buffer)); |
599 | bzero((char*) length, sizeof(length)); |
600 | bzero((char*) is_null, sizeof(is_null)); |
601 | |
602 | for(i= 0; i < field_count; i++) |
603 | { |
604 | buffer[i].buffer_type= MYSQL_TYPE_STRING; |
605 | buffer[i].buffer_length= MAX_FIELD_DATA_SIZE; |
606 | buffer[i].length= &length[i]; |
607 | buffer[i].buffer= (void *) data[i]; |
608 | buffer[i].is_null= &is_null[i]; |
609 | } |
610 | |
611 | rc= mysql_stmt_bind_result(stmt, buffer); |
612 | check_execute(stmt, rc); |
613 | |
614 | rc= 1; |
615 | mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*)&rc); |
616 | rc= mysql_stmt_store_result(stmt); |
617 | check_execute(stmt, rc); |
618 | my_print_result_metadata(result); |
619 | |
620 | mysql_field_seek(result, 0); |
621 | while ((rc= mysql_stmt_fetch(stmt)) == 0) |
622 | { |
623 | if (!opt_silent) |
624 | { |
625 | fputc('\t', stdout); |
626 | fputc('|', stdout); |
627 | } |
628 | mysql_field_seek(result, 0); |
629 | for (i= 0; i < field_count; i++) |
630 | { |
631 | field= mysql_fetch_field(result); |
632 | if (!opt_silent) |
633 | { |
634 | if (is_null[i]) |
635 | fprintf(stdout, " %-*s |" , (int) field->max_length, "NULL" ); |
636 | else if (length[i] == 0) |
637 | { |
638 | data[i][0]= '\0'; /* unmodified buffer */ |
639 | fprintf(stdout, " %*s |" , (int) field->max_length, data[i]); |
640 | } |
641 | else if (IS_NUM(field->type)) |
642 | fprintf(stdout, " %*s |" , (int) field->max_length, data[i]); |
643 | else |
644 | fprintf(stdout, " %-*s |" , (int) field->max_length, data[i]); |
645 | } |
646 | } |
647 | if (!opt_silent) |
648 | { |
649 | fputc('\t', stdout); |
650 | fputc('\n', stdout); |
651 | } |
652 | row_count++; |
653 | } |
654 | DIE_UNLESS(rc == MYSQL_NO_DATA); |
655 | if (!opt_silent) |
656 | { |
657 | if (row_count) |
658 | my_print_dashes(result); |
659 | fprintf(stdout, "\n\t%d %s returned\n" , row_count, |
660 | row_count == 1 ? "row" : "rows" ); |
661 | } |
662 | mysql_free_result(result); |
663 | return row_count; |
664 | } |
665 | |
666 | |
667 | /* Prepare statement, execute, and process result set for given query */ |
668 | |
669 | int my_stmt_result(const char *buff) |
670 | { |
671 | MYSQL_STMT *stmt; |
672 | int row_count; |
673 | int rc; |
674 | |
675 | if (!opt_silent) |
676 | fprintf(stdout, "\n\n %s" , buff); |
677 | stmt= mysql_simple_prepare(mysql, buff); |
678 | check_stmt(stmt); |
679 | |
680 | rc= mysql_stmt_execute(stmt); |
681 | check_execute(stmt, rc); |
682 | |
683 | row_count= my_process_stmt_result(stmt); |
684 | mysql_stmt_close(stmt); |
685 | |
686 | return row_count; |
687 | } |
688 | |
689 | /* Print the total number of warnings and the warnings themselves. */ |
690 | |
691 | void my_process_warnings(MYSQL *conn, unsigned expected_warning_count) |
692 | { |
693 | MYSQL_RES *result; |
694 | int rc; |
695 | |
696 | if (!opt_silent) |
697 | fprintf(stdout, "\n total warnings: %u (expected: %u)\n" , |
698 | mysql_warning_count(conn), expected_warning_count); |
699 | |
700 | DIE_UNLESS(mysql_warning_count(mysql) == expected_warning_count); |
701 | |
702 | rc= mysql_query(conn, "SHOW WARNINGS" ); |
703 | DIE_UNLESS(rc == 0); |
704 | |
705 | result= mysql_store_result(conn); |
706 | mytest(result); |
707 | |
708 | rc= my_process_result_set(result); |
709 | mysql_free_result(result); |
710 | } |
711 | |
712 | |
713 | /* Utility function to verify a particular column data */ |
714 | |
715 | static void verify_col_data(const char *table, const char *col, |
716 | const char *exp_data) |
717 | { |
718 | static char query[MAX_TEST_QUERY_LENGTH]; |
719 | MYSQL_RES *result; |
720 | MYSQL_ROW row; |
721 | int rc, field= 1; |
722 | |
723 | if (table && col) |
724 | { |
725 | strxmov(query, "SELECT " , col, " FROM " , table, " LIMIT 1" , NullS); |
726 | if (!opt_silent) |
727 | fprintf(stdout, "\n %s" , query); |
728 | rc= mysql_query(mysql, query); |
729 | myquery(rc); |
730 | |
731 | field= 0; |
732 | } |
733 | |
734 | result= mysql_use_result(mysql); |
735 | mytest(result); |
736 | |
737 | if (!(row= mysql_fetch_row(result)) || !row[field]) |
738 | { |
739 | fprintf(stdout, "\n *** ERROR: FAILED TO GET THE RESULT ***" ); |
740 | exit(1); |
741 | } |
742 | if (strcmp(row[field], exp_data)) |
743 | { |
744 | fprintf(stdout, "\n obtained: `%s` (expected: `%s`)" , |
745 | row[field], exp_data); |
746 | DIE_UNLESS(FALSE); |
747 | } |
748 | mysql_free_result(result); |
749 | } |
750 | |
751 | |
752 | /* Utility function to verify the field members */ |
753 | |
754 | #define verify_prepare_field(result,no,name,org_name,type,table,\ |
755 | org_table,db,length,def) \ |
756 | do_verify_prepare_field((result),(no),(name),(org_name),(type), \ |
757 | (table),(org_table),(db),(length),(def), \ |
758 | __FILE__, __LINE__) |
759 | |
760 | static void do_verify_prepare_field(MYSQL_RES *result, |
761 | unsigned int no, const char *name, |
762 | const char *org_name, |
763 | enum enum_field_types type, |
764 | const char *table, |
765 | const char *org_table, const char *db, |
766 | unsigned long length, const char *def, |
767 | const char *file, int line) |
768 | { |
769 | MYSQL_FIELD *field; |
770 | CHARSET_INFO *cs; |
771 | ulonglong expected_field_length= length; |
772 | |
773 | if (!(field= mysql_fetch_field_direct(result, no))) |
774 | { |
775 | fprintf(stdout, "\n *** ERROR: FAILED TO GET THE RESULT ***" ); |
776 | exit(1); |
777 | } |
778 | cs= get_charset(field->charsetnr, 0); |
779 | DIE_UNLESS(cs); |
780 | if ((expected_field_length*= cs->mbmaxlen) > UINT_MAX32) |
781 | expected_field_length= UINT_MAX32; |
782 | if (!opt_silent) |
783 | { |
784 | fprintf(stdout, "\n field[%d]:" , no); |
785 | fprintf(stdout, "\n name :`%s`\t(expected: `%s`)" , field->name, name); |
786 | fprintf(stdout, "\n org_name :`%s`\t(expected: `%s`)" , |
787 | field->org_name, org_name); |
788 | fprintf(stdout, "\n type :`%d`\t(expected: `%d`)" , field->type, type); |
789 | if (table) |
790 | fprintf(stdout, "\n table :`%s`\t(expected: `%s`)" , |
791 | field->table, table); |
792 | if (org_table) |
793 | fprintf(stdout, "\n org_table:`%s`\t(expected: `%s`)" , |
794 | field->org_table, org_table); |
795 | fprintf(stdout, "\n database :`%s`\t(expected: `%s`)" , field->db, db); |
796 | fprintf(stdout, "\n length :`%lu`\t(expected: `%llu`)" , |
797 | field->length, expected_field_length); |
798 | fprintf(stdout, "\n maxlength:`%ld`" , field->max_length); |
799 | fprintf(stdout, "\n charsetnr:`%d`" , field->charsetnr); |
800 | fprintf(stdout, "\n default :`%s`\t(expected: `%s`)" , |
801 | field->def ? field->def : "(null)" , def ? def: "(null)" ); |
802 | fprintf(stdout, "\n" ); |
803 | } |
804 | DIE_UNLESS(strcmp(field->name, name) == 0); |
805 | DIE_UNLESS(strcmp(field->org_name, org_name) == 0); |
806 | /* |
807 | XXX: silent column specification change works based on number of |
808 | bytes a column occupies. So CHAR -> VARCHAR upgrade is possible even |
809 | for CHAR(2) column if its character set is multibyte. |
810 | VARCHAR -> CHAR downgrade won't work for VARCHAR(3) as one would |
811 | expect. |
812 | */ |
813 | if (cs->mbmaxlen == 1) |
814 | { |
815 | if (field->type != type) |
816 | { |
817 | fprintf(stderr, |
818 | "Expected field type: %d, got type: %d in file %s, line %d\n" , |
819 | (int) type, (int) field->type, file, line); |
820 | DIE_UNLESS(field->type == type); |
821 | } |
822 | } |
823 | if (table) |
824 | DIE_UNLESS(strcmp(field->table, table) == 0); |
825 | if (org_table) |
826 | DIE_UNLESS(strcmp(field->org_table, org_table) == 0); |
827 | DIE_UNLESS(strcmp(field->db, db) == 0); |
828 | /* |
829 | Character set should be taken into account for multibyte encodings, such |
830 | as utf8. Field length is calculated as number of characters * maximum |
831 | number of bytes a character can occupy. |
832 | */ |
833 | if (length && (field->length != expected_field_length)) |
834 | { |
835 | fflush(stdout); |
836 | fprintf(stderr, "Expected field length: %llu, got length: %lu\n" , |
837 | expected_field_length, field->length); |
838 | fflush(stderr); |
839 | DIE_UNLESS(field->length == expected_field_length); |
840 | } |
841 | if (def) |
842 | DIE_UNLESS(strcmp(field->def, def) == 0); |
843 | } |
844 | |
845 | |
846 | /* Utility function to verify the parameter count */ |
847 | |
848 | static void verify_param_count(MYSQL_STMT *stmt, long exp_count) |
849 | { |
850 | long param_count= mysql_stmt_param_count(stmt); |
851 | if (!opt_silent) |
852 | fprintf(stdout, "\n total parameters in stmt: `%ld` (expected: `%ld`)" , |
853 | param_count, exp_count); |
854 | DIE_UNLESS(param_count == exp_count); |
855 | } |
856 | |
857 | |
858 | /* Utility function to verify the total affected rows */ |
859 | |
860 | static void verify_st_affected_rows(MYSQL_STMT *stmt, ulonglong exp_count) |
861 | { |
862 | ulonglong affected_rows= mysql_stmt_affected_rows(stmt); |
863 | if (!opt_silent) |
864 | fprintf(stdout, "\n total affected rows: `%ld` (expected: `%ld`)" , |
865 | (long) affected_rows, (long) exp_count); |
866 | DIE_UNLESS(affected_rows == exp_count); |
867 | } |
868 | |
869 | |
870 | /* Utility function to verify the total affected rows */ |
871 | |
872 | static void verify_affected_rows(ulonglong exp_count) |
873 | { |
874 | ulonglong affected_rows= mysql_affected_rows(mysql); |
875 | if (!opt_silent) |
876 | fprintf(stdout, "\n total affected rows: `%ld` (expected: `%ld`)" , |
877 | (long) affected_rows, (long) exp_count); |
878 | DIE_UNLESS(affected_rows == exp_count); |
879 | } |
880 | |
881 | |
882 | /* Utility function to verify the total fields count */ |
883 | |
884 | static void verify_field_count(MYSQL_RES *result, uint exp_count) |
885 | { |
886 | uint field_count= mysql_num_fields(result); |
887 | if (!opt_silent) |
888 | fprintf(stdout, "\n total fields in the result set: `%d` (expected: `%d`)" , |
889 | field_count, exp_count); |
890 | DIE_UNLESS(field_count == exp_count); |
891 | } |
892 | |
893 | |
894 | /* Utility function to execute a query using prepare-execute */ |
895 | |
896 | #ifndef EMBEDDED_LIBRARY |
897 | static void execute_prepare_query(const char *query, ulonglong exp_count) |
898 | { |
899 | MYSQL_STMT *stmt; |
900 | ulonglong affected_rows; |
901 | int rc; |
902 | |
903 | stmt= mysql_simple_prepare(mysql, query); |
904 | check_stmt(stmt); |
905 | |
906 | rc= mysql_stmt_execute(stmt); |
907 | myquery(rc); |
908 | |
909 | affected_rows= mysql_stmt_affected_rows(stmt); |
910 | if (!opt_silent) |
911 | fprintf(stdout, "\n total affected rows: `%ld` (expected: `%ld`)" , |
912 | (long) affected_rows, (long) exp_count); |
913 | |
914 | DIE_UNLESS(affected_rows == exp_count); |
915 | mysql_stmt_close(stmt); |
916 | } |
917 | #endif |
918 | |
919 | /* |
920 | Accepts arbitrary number of queries and runs them against the database. |
921 | Used to fill tables for each test. |
922 | */ |
923 | |
924 | void fill_tables(const char **query_list, unsigned query_count) |
925 | { |
926 | int rc; |
927 | const char **query; |
928 | DBUG_ENTER("fill_tables" ); |
929 | for (query= query_list; query < query_list + query_count; |
930 | ++query) |
931 | { |
932 | rc= mysql_query(mysql, *query); |
933 | myquery(rc); |
934 | } |
935 | DBUG_VOID_RETURN; |
936 | } |
937 | |
938 | /* |
939 | All state of fetch from one statement: statement handle, out buffers, |
940 | fetch position. |
941 | See fetch_n for for the only use case. |
942 | */ |
943 | |
944 | enum { MAX_COLUMN_LENGTH= 255 }; |
945 | |
946 | typedef struct st_stmt_fetch |
947 | { |
948 | const char *query; |
949 | unsigned stmt_no; |
950 | MYSQL_STMT *handle; |
951 | my_bool is_open; |
952 | MYSQL_BIND *bind_array; |
953 | char **out_data; |
954 | unsigned long *out_data_length; |
955 | unsigned column_count; |
956 | unsigned row_count; |
957 | } Stmt_fetch; |
958 | |
959 | |
960 | /* |
961 | Create statement handle, prepare it with statement, execute and allocate |
962 | fetch buffers. |
963 | */ |
964 | |
965 | void stmt_fetch_init(Stmt_fetch *fetch, unsigned stmt_no_arg, |
966 | const char *query_arg) |
967 | { |
968 | unsigned long type= CURSOR_TYPE_READ_ONLY; |
969 | int rc; |
970 | unsigned i; |
971 | MYSQL_RES *metadata; |
972 | DBUG_ENTER("stmt_fetch_init" ); |
973 | |
974 | /* Save query and statement number for error messages */ |
975 | fetch->stmt_no= stmt_no_arg; |
976 | fetch->query= query_arg; |
977 | |
978 | fetch->handle= mysql_stmt_init(mysql); |
979 | |
980 | rc= mysql_stmt_prepare(fetch->handle, fetch->query, (ulong)strlen(fetch->query)); |
981 | check_execute(fetch->handle, rc); |
982 | |
983 | /* |
984 | The attribute is sent to server on execute and asks to open read-only |
985 | for result set |
986 | */ |
987 | mysql_stmt_attr_set(fetch->handle, STMT_ATTR_CURSOR_TYPE, |
988 | (const void*) &type); |
989 | |
990 | rc= mysql_stmt_execute(fetch->handle); |
991 | check_execute(fetch->handle, rc); |
992 | |
993 | /* Find out total number of columns in result set */ |
994 | metadata= mysql_stmt_result_metadata(fetch->handle); |
995 | fetch->column_count= mysql_num_fields(metadata); |
996 | mysql_free_result(metadata); |
997 | |
998 | /* |
999 | Now allocate bind handles and buffers for output data: |
1000 | calloc memory to reduce number of MYSQL_BIND members we need to |
1001 | set up. |
1002 | */ |
1003 | |
1004 | fetch->bind_array= (MYSQL_BIND *) calloc(1, sizeof(MYSQL_BIND) * |
1005 | fetch->column_count); |
1006 | fetch->out_data= (char**) calloc(1, sizeof(char*) * fetch->column_count); |
1007 | fetch->out_data_length= (ulong*) calloc(1, sizeof(ulong) * |
1008 | fetch->column_count); |
1009 | for (i= 0; i < fetch->column_count; ++i) |
1010 | { |
1011 | fetch->out_data[i]= (char*) calloc(1, MAX_COLUMN_LENGTH); |
1012 | fetch->bind_array[i].buffer_type= MYSQL_TYPE_STRING; |
1013 | fetch->bind_array[i].buffer= fetch->out_data[i]; |
1014 | fetch->bind_array[i].buffer_length= MAX_COLUMN_LENGTH; |
1015 | fetch->bind_array[i].length= fetch->out_data_length + i; |
1016 | } |
1017 | |
1018 | mysql_stmt_bind_result(fetch->handle, fetch->bind_array); |
1019 | |
1020 | fetch->row_count= 0; |
1021 | fetch->is_open= TRUE; |
1022 | |
1023 | /* Ready for reading rows */ |
1024 | DBUG_VOID_RETURN; |
1025 | } |
1026 | |
1027 | |
1028 | /* Fetch and print one row from cursor */ |
1029 | |
1030 | int stmt_fetch_fetch_row(Stmt_fetch *fetch) |
1031 | { |
1032 | int rc; |
1033 | unsigned i; |
1034 | DBUG_ENTER("stmt_fetch_fetch_row" ); |
1035 | |
1036 | if ((rc= mysql_stmt_fetch(fetch->handle)) == 0) |
1037 | { |
1038 | ++fetch->row_count; |
1039 | if (!opt_silent) |
1040 | printf("Stmt %d fetched row %d:\n" , fetch->stmt_no, fetch->row_count); |
1041 | for (i= 0; i < fetch->column_count; ++i) |
1042 | { |
1043 | fetch->out_data[i][fetch->out_data_length[i]]= '\0'; |
1044 | if (!opt_silent) |
1045 | printf("column %d: %s\n" , i+1, fetch->out_data[i]); |
1046 | } |
1047 | } |
1048 | else |
1049 | fetch->is_open= FALSE; |
1050 | DBUG_RETURN(rc); |
1051 | } |
1052 | |
1053 | |
1054 | void stmt_fetch_close(Stmt_fetch *fetch) |
1055 | { |
1056 | unsigned i; |
1057 | DBUG_ENTER("stmt_fetch_close" ); |
1058 | |
1059 | for (i= 0; i < fetch->column_count; ++i) |
1060 | free(fetch->out_data[i]); |
1061 | free(fetch->out_data); |
1062 | free(fetch->out_data_length); |
1063 | free(fetch->bind_array); |
1064 | mysql_stmt_close(fetch->handle); |
1065 | DBUG_VOID_RETURN; |
1066 | } |
1067 | |
1068 | /* |
1069 | For given array of queries, open query_count cursors and fetch |
1070 | from them in simultaneous manner. |
1071 | In case there was an error in one of the cursors, continue |
1072 | reading from the rest. |
1073 | */ |
1074 | |
1075 | enum fetch_type { USE_ROW_BY_ROW_FETCH= 0, USE_STORE_RESULT= 1 }; |
1076 | |
1077 | my_bool fetch_n(const char **query_list, unsigned query_count, |
1078 | enum fetch_type fetch_type) |
1079 | { |
1080 | unsigned open_statements= query_count; |
1081 | int rc, error_count= 0; |
1082 | Stmt_fetch *fetch_array= (Stmt_fetch*) calloc(1, sizeof(Stmt_fetch) * |
1083 | query_count); |
1084 | Stmt_fetch *fetch; |
1085 | DBUG_ENTER("fetch_n" ); |
1086 | |
1087 | for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch) |
1088 | { |
1089 | /* Init will exit(1) in case of error */ |
1090 | stmt_fetch_init(fetch, (uint)(fetch - fetch_array), |
1091 | query_list[fetch - fetch_array]); |
1092 | } |
1093 | |
1094 | if (fetch_type == USE_STORE_RESULT) |
1095 | { |
1096 | for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch) |
1097 | { |
1098 | rc= mysql_stmt_store_result(fetch->handle); |
1099 | check_execute(fetch->handle, rc); |
1100 | } |
1101 | } |
1102 | |
1103 | while (open_statements) |
1104 | { |
1105 | for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch) |
1106 | { |
1107 | if (fetch->is_open && (rc= stmt_fetch_fetch_row(fetch))) |
1108 | { |
1109 | open_statements--; |
1110 | /* |
1111 | We try to fetch from the rest of the statements in case of |
1112 | error |
1113 | */ |
1114 | if (rc != MYSQL_NO_DATA) |
1115 | { |
1116 | fprintf(stderr, |
1117 | "Got error reading rows from statement %d,\n" |
1118 | "query is: %s,\n" |
1119 | "error message: %s" , (int) (fetch - fetch_array), |
1120 | fetch->query, |
1121 | mysql_stmt_error(fetch->handle)); |
1122 | error_count++; |
1123 | } |
1124 | } |
1125 | } |
1126 | } |
1127 | if (error_count) |
1128 | fprintf(stderr, "Fetch FAILED" ); |
1129 | else |
1130 | { |
1131 | unsigned total_row_count= 0; |
1132 | for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch) |
1133 | total_row_count+= fetch->row_count; |
1134 | if (!opt_silent) |
1135 | printf("Success, total rows fetched: %d\n" , total_row_count); |
1136 | } |
1137 | for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch) |
1138 | stmt_fetch_close(fetch); |
1139 | free(fetch_array); |
1140 | DBUG_RETURN(error_count != 0); |
1141 | } |
1142 | |
1143 | /* Separate thread query to test some cases */ |
1144 | |
1145 | static my_bool thread_query(const char *query) |
1146 | { |
1147 | MYSQL *l_mysql; |
1148 | my_bool error; |
1149 | my_bool reconnect= 1; |
1150 | error= 0; |
1151 | if (!opt_silent) |
1152 | fprintf(stdout, "\n in thread_query(%s)" , query); |
1153 | if (!(l_mysql= mysql_client_init(NULL))) |
1154 | { |
1155 | myerror("mysql_client_init() failed" ); |
1156 | return 1; |
1157 | } |
1158 | if (!(mysql_real_connect(l_mysql, opt_host, opt_user, |
1159 | opt_password, current_db, opt_port, |
1160 | opt_unix_socket, 0))) |
1161 | { |
1162 | myerror("connection failed" ); |
1163 | error= 1; |
1164 | goto end; |
1165 | } |
1166 | mysql_options(l_mysql, MYSQL_OPT_RECONNECT, &reconnect); |
1167 | if (mysql_query(l_mysql, query)) |
1168 | { |
1169 | fprintf(stderr, "Query failed (%s)\n" , mysql_error(l_mysql)); |
1170 | error= 1; |
1171 | goto end; |
1172 | } |
1173 | mysql_commit(l_mysql); |
1174 | end: |
1175 | mysql_close(l_mysql); |
1176 | return error; |
1177 | } |
1178 | |
1179 | |
1180 | static int mysql_query_or_error(MYSQL *mysql, const char *query) |
1181 | { |
1182 | int rc= mysql_query(mysql, query); |
1183 | if (rc) |
1184 | fprintf(stderr, "ERROR %d: %s" , mysql_errno(mysql), mysql_error(mysql)); |
1185 | return rc; |
1186 | } |
1187 | |
1188 | |
1189 | /* |
1190 | Read and parse arguments and MySQL options from my.cnf |
1191 | */ |
1192 | |
1193 | static const char *client_test_load_default_groups[]= |
1194 | { "client" , "client-server" , "client-mariadb" , 0 }; |
1195 | static char **defaults_argv; |
1196 | |
1197 | static struct my_option client_test_long_options[] = |
1198 | { |
1199 | {"basedir" , 'b', "Basedir for tests." ,(void *)&opt_basedir, |
1200 | (void *)&opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
1201 | {"character-sets-dir" , 'C', |
1202 | "Directory for character set files." , (void *)&charsets_dir, |
1203 | (void *)&charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
1204 | {"count" , 't', "Number of times test to be executed" , &opt_count_read, |
1205 | &opt_count_read, 0, GET_UINT, REQUIRED_ARG, 1, 0, 0, 0, 0, 0}, |
1206 | {"database" , 'D', "Database to use" , &opt_db, &opt_db, |
1207 | 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
1208 | {"do-not-drop-database" , 'd', "Do not drop database while disconnecting" , |
1209 | 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, |
1210 | {"debug" , '#', "Output debug log" , (void *)&default_dbug_option, |
1211 | (void *)&default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, |
1212 | {"help" , '?', "Display this help and exit" , 0, 0, 0, GET_NO_ARG, NO_ARG, 0, |
1213 | 0, 0, 0, 0, 0}, |
1214 | {"host" , 'h', "Connect to host" , &opt_host, &opt_host, |
1215 | 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
1216 | {"password" , 'p', |
1217 | "Password to use when connecting to server. If password is not given it's asked from the tty." , |
1218 | 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, |
1219 | {"port" , 'P', "Port number to use for connection or 0 for default to, in " |
1220 | "order of preference, my.cnf, $MYSQL_TCP_PORT, " |
1221 | #if MYSQL_PORT_DEFAULT == 0 |
1222 | "/etc/services, " |
1223 | #endif |
1224 | "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ")." , |
1225 | &opt_port, &opt_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
1226 | {"server-arg" , 'A', "Send embedded server this as a parameter." , |
1227 | 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
1228 | {"show-tests" , 'T', "Show all tests' names" , 0, 0, 0, GET_NO_ARG, NO_ARG, |
1229 | 0, 0, 0, 0, 0, 0}, |
1230 | {"silent" , 's', "Be more silent" , 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, |
1231 | 0}, |
1232 | #ifdef HAVE_SMEM |
1233 | {"shared-memory-base-name" , 'm', "Base name of shared memory." , |
1234 | &shared_memory_base_name, (uchar**)&shared_memory_base_name, 0, |
1235 | GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
1236 | #endif |
1237 | {"socket" , 'S', "Socket file to use for connection" , |
1238 | &opt_unix_socket, &opt_unix_socket, 0, GET_STR, |
1239 | REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
1240 | {"testcase" , 'c', |
1241 | "May disable some code when runs as mysql-test-run testcase." , |
1242 | 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, |
1243 | #ifndef DONT_ALLOW_USER_CHANGE |
1244 | {"user" , 'u', "User for login if not current user" , &opt_user, |
1245 | &opt_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
1246 | #endif |
1247 | {"vardir" , 'v', "Data dir for tests." , (void *)&opt_vardir, |
1248 | (void *)&opt_vardir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
1249 | {"non-blocking-api" , 'n', |
1250 | "Use the non-blocking client API for communication." , |
1251 | &non_blocking_api_enabled, &non_blocking_api_enabled, 0, |
1252 | GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
1253 | {"getopt-ll-test" , 'g', "Option for testing bug in getopt library" , |
1254 | &opt_getopt_ll_test, &opt_getopt_ll_test, 0, |
1255 | GET_LL, REQUIRED_ARG, 0, 0, LONGLONG_MAX, 0, 0, 0}, |
1256 | {"plugin_dir" , 0, "Directory for client-side plugins." , |
1257 | &opt_plugin_dir, &opt_plugin_dir, 0, |
1258 | GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
1259 | {"default_auth" , 0, "Default authentication client-side plugin to use." , |
1260 | &opt_default_auth, &opt_default_auth, 0, |
1261 | GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
1262 | { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} |
1263 | }; |
1264 | |
1265 | |
1266 | static void usage(void) |
1267 | { |
1268 | /* show the usage string when the user asks for this */ |
1269 | putc('\n', stdout); |
1270 | printf("%s Ver %s Distrib %s, for %s (%s)\n" , |
1271 | my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE); |
1272 | puts("By Monty, Venu, Kent and others\n" ); |
1273 | printf("\ |
1274 | Copyright (C) 2002-2004 MySQL AB\n\ |
1275 | This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n\ |
1276 | and you are welcome to modify and redistribute it under the GPL license\n" ); |
1277 | printf("Usage: %s [OPTIONS] [TESTNAME1 TESTNAME2...]\n" , my_progname); |
1278 | my_print_help(client_test_long_options); |
1279 | print_defaults("my" , client_test_load_default_groups); |
1280 | my_print_variables(client_test_long_options); |
1281 | } |
1282 | |
1283 | static struct my_tests_st *get_my_tests(); /* To be defined in main .c file */ |
1284 | |
1285 | static struct my_tests_st *my_testlist= 0; |
1286 | |
1287 | static my_bool |
1288 | get_one_option(int optid, const struct my_option *opt __attribute__((unused)), |
1289 | char *argument) |
1290 | { |
1291 | switch (optid) { |
1292 | case '#': |
1293 | DBUG_PUSH(argument ? argument : default_dbug_option); |
1294 | break; |
1295 | case 'c': |
1296 | opt_testcase = 1; |
1297 | break; |
1298 | case 'p': |
1299 | if (argument) |
1300 | { |
1301 | char *start=argument; |
1302 | my_free(opt_password); |
1303 | opt_password= my_strdup(argument, MYF(MY_FAE)); |
1304 | while (*argument) *argument++= 'x'; /* Destroy argument */ |
1305 | if (*start) |
1306 | start[1]=0; |
1307 | } |
1308 | else |
1309 | tty_password= 1; |
1310 | break; |
1311 | case 's': |
1312 | if (argument == disabled_my_option) |
1313 | opt_silent= 0; |
1314 | else |
1315 | opt_silent++; |
1316 | break; |
1317 | case 'd': |
1318 | opt_drop_db= 0; |
1319 | break; |
1320 | case 'A': |
1321 | /* |
1322 | When the embedded server is being tested, the test suite needs to be |
1323 | able to pass command-line arguments to the embedded server so it can |
1324 | locate the language files and data directory. The test suite |
1325 | (mysql-test-run) never uses config files, just command-line options. |
1326 | */ |
1327 | if (!embedded_server_arg_count) |
1328 | { |
1329 | embedded_server_arg_count= 1; |
1330 | embedded_server_args[0]= (char*) "" ; |
1331 | } |
1332 | if (embedded_server_arg_count == MAX_SERVER_ARGS-1 || |
1333 | !(embedded_server_args[embedded_server_arg_count++]= |
1334 | my_strdup(argument, MYF(MY_FAE)))) |
1335 | { |
1336 | DIE("Can't use server argument" ); |
1337 | } |
1338 | break; |
1339 | case 'T': |
1340 | { |
1341 | struct my_tests_st *fptr; |
1342 | |
1343 | printf("All possible test names:\n\n" ); |
1344 | for (fptr= my_testlist; fptr->name; fptr++) |
1345 | printf("%s\n" , fptr->name); |
1346 | exit(0); |
1347 | break; |
1348 | } |
1349 | case 'C': |
1350 | strmake_buf(mysql_charsets_dir, argument); |
1351 | charsets_dir = mysql_charsets_dir; |
1352 | break; |
1353 | case '?': |
1354 | case 'I': /* Info */ |
1355 | usage(); |
1356 | exit(0); |
1357 | break; |
1358 | } |
1359 | return 0; |
1360 | } |
1361 | |
1362 | static void get_options(int *argc, char ***argv) |
1363 | { |
1364 | int ho_error; |
1365 | /* Copy argv from load_defaults, so we can free it when done. */ |
1366 | defaults_argv= *argv; |
1367 | /* reset --silent option */ |
1368 | opt_silent= 0; |
1369 | |
1370 | if ((ho_error= handle_options(argc, argv, client_test_long_options, |
1371 | get_one_option))) |
1372 | exit(ho_error); |
1373 | |
1374 | if (tty_password) |
1375 | opt_password= get_tty_password(NullS); |
1376 | return; |
1377 | } |
1378 | |
1379 | /* |
1380 | Print the test output on successful execution before exiting |
1381 | */ |
1382 | |
1383 | static void print_test_output() |
1384 | { |
1385 | if (opt_silent < 3) |
1386 | { |
1387 | fprintf(stdout, "\n\n" ); |
1388 | fprintf(stdout, "All '%d' tests were successful (in '%d' iterations)" , |
1389 | test_count-1, opt_count); |
1390 | if (!opt_silent) |
1391 | { |
1392 | fprintf(stdout, "\n Total execution time: %g SECS" , total_time); |
1393 | if (opt_count > 1) |
1394 | fprintf(stdout, " (Avg: %g SECS)" , total_time/opt_count); |
1395 | } |
1396 | |
1397 | fprintf(stdout, "\n\n!!! SUCCESS !!!\n" ); |
1398 | } |
1399 | } |
1400 | |
1401 | /*************************************************************************** |
1402 | main routine |
1403 | ***************************************************************************/ |
1404 | |
1405 | |
1406 | int main(int argc, char **argv) |
1407 | { |
1408 | int i; |
1409 | char **tests_to_run= NULL, **curr_test; |
1410 | struct my_tests_st *fptr; |
1411 | my_testlist= get_my_tests(); |
1412 | |
1413 | MY_INIT(argv[0]); |
1414 | /* Copy the original arguments, so it can be reused for restarting. */ |
1415 | original_argc= argc; |
1416 | original_argv= malloc(argc * sizeof(char*)); |
1417 | if (argc && !original_argv) |
1418 | exit(1); |
1419 | for (i= 0; i < argc; i++) |
1420 | original_argv[i]= strdup(argv[i]); |
1421 | |
1422 | load_defaults_or_exit("my" , client_test_load_default_groups, &argc, &argv); |
1423 | |
1424 | get_options(&argc, &argv); |
1425 | /* Set main opt_count. */ |
1426 | opt_count= opt_count_read; |
1427 | |
1428 | /* If there are any arguments left (named tests), save them. */ |
1429 | if (argc) |
1430 | { |
1431 | tests_to_run= malloc((argc + 1) * sizeof(char*)); |
1432 | if (!tests_to_run) |
1433 | exit(1); |
1434 | for (i= 0; i < argc; i++) |
1435 | tests_to_run[i]= strdup(argv[i]); |
1436 | tests_to_run[i]= NULL; |
1437 | } |
1438 | |
1439 | if (mysql_server_init(embedded_server_arg_count, |
1440 | embedded_server_args, |
1441 | (char**) embedded_server_groups)) |
1442 | DIE("Can't initialize MySQL server" ); |
1443 | |
1444 | /* connect to server with no flags, default protocol, auto reconnect true */ |
1445 | mysql= client_connect(0, MYSQL_PROTOCOL_DEFAULT, 1); |
1446 | |
1447 | total_time= 0; |
1448 | for (iter_count= 1; iter_count <= opt_count; iter_count++) |
1449 | { |
1450 | /* Start of tests */ |
1451 | test_count= 1; |
1452 | start_time= time((time_t *)0); |
1453 | if (!tests_to_run) |
1454 | { |
1455 | for (fptr= my_testlist; fptr->name; fptr++) |
1456 | (*fptr->function)(); |
1457 | } |
1458 | else |
1459 | { |
1460 | for (curr_test= tests_to_run ; *curr_test ; curr_test++) |
1461 | { |
1462 | for (fptr= my_testlist; fptr->name; fptr++) |
1463 | { |
1464 | if (!strcmp(fptr->name, *curr_test)) |
1465 | { |
1466 | (*fptr->function)(); |
1467 | break; |
1468 | } |
1469 | } |
1470 | if (!fptr->name) |
1471 | { |
1472 | fprintf(stderr, "\n\nGiven test not found: '%s'\n" , *argv); |
1473 | fprintf(stderr, "See legal test names with %s -T\n\nAborting!\n" , |
1474 | my_progname); |
1475 | client_disconnect(mysql); |
1476 | free_defaults(defaults_argv); |
1477 | mysql_server_end(); |
1478 | exit(1); |
1479 | } |
1480 | } |
1481 | } |
1482 | |
1483 | end_time= time((time_t *)0); |
1484 | total_time+= difftime(end_time, start_time); |
1485 | |
1486 | /* End of tests */ |
1487 | } |
1488 | |
1489 | client_disconnect(mysql); /* disconnect from server */ |
1490 | |
1491 | free_defaults(defaults_argv); |
1492 | print_test_output(); |
1493 | |
1494 | while (embedded_server_arg_count > 1) |
1495 | my_free(embedded_server_args[--embedded_server_arg_count]); |
1496 | |
1497 | mysql_server_end(); |
1498 | |
1499 | my_end(0); |
1500 | |
1501 | for (i= 0; i < original_argc; i++) |
1502 | free(original_argv[i]); |
1503 | if (original_argc) |
1504 | free(original_argv); |
1505 | if (tests_to_run) |
1506 | { |
1507 | for (curr_test= tests_to_run ; *curr_test ; curr_test++) |
1508 | free(*curr_test); |
1509 | free(tests_to_run); |
1510 | } |
1511 | my_free(opt_password); |
1512 | my_free(opt_host); |
1513 | |
1514 | exit(0); |
1515 | } |
1516 | |