1 | /* |
2 | Copyright (c) 2005, 2015, Oracle and/or its affiliates. |
3 | Copyright (c) 2010, 2017, MariaDB |
4 | |
5 | This program is free software; you can redistribute it and/or modify |
6 | it under the terms of the GNU General Public License as published by |
7 | the Free Software Foundation; version 2 of the License. |
8 | |
9 | This program is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License |
15 | along with this program; if not, write to the Free Software |
16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
17 | */ |
18 | |
19 | /* |
20 | MySQL Slap |
21 | |
22 | A simple program designed to work as if multiple clients querying the database, |
23 | then reporting the timing of each stage. |
24 | |
25 | MySQL slap runs three stages: |
26 | 1) Create schema,table, and optionally any SP or data you want to beign |
27 | the test with. (single client) |
28 | 2) Load test (many clients) |
29 | 3) Cleanup (disconnection, drop table if specified, single client) |
30 | |
31 | Examples: |
32 | |
33 | Supply your own create and query SQL statements, with 50 clients |
34 | querying (200 selects for each): |
35 | |
36 | mysqlslap --delimiter=";" \ |
37 | --create="CREATE TABLE A (a int);INSERT INTO A VALUES (23)" \ |
38 | --query="SELECT * FROM A" --concurrency=50 --iterations=200 |
39 | |
40 | Let the program build the query SQL statement with a table of two int |
41 | columns, three varchar columns, five clients querying (20 times each), |
42 | don't create the table or insert the data (using the previous test's |
43 | schema and data): |
44 | |
45 | mysqlslap --concurrency=5 --iterations=20 \ |
46 | --number-int-cols=2 --number-char-cols=3 \ |
47 | --auto-generate-sql |
48 | |
49 | Tell the program to load the create, insert and query SQL statements from |
50 | the specified files, where the create.sql file has multiple table creation |
51 | statements delimited by ';' and multiple insert statements delimited by ';'. |
52 | The --query file will have multiple queries delimited by ';', run all the |
53 | load statements, and then run all the queries in the query file |
54 | with five clients (five times each): |
55 | |
56 | mysqlslap --concurrency=5 \ |
57 | --iterations=5 --query=query.sql --create=create.sql \ |
58 | --delimiter=";" |
59 | |
60 | TODO: |
61 | Add language for better tests |
62 | String length for files and those put on the command line are not |
63 | setup to handle binary data. |
64 | More stats |
65 | Break up tests and run them on multiple hosts at once. |
66 | Allow output to be fed into a database directly. |
67 | |
68 | */ |
69 | |
70 | #define SLAP_VERSION "1.0" |
71 | |
72 | #define HUGE_STRING_LENGTH 8196 |
73 | #define RAND_STRING_SIZE 126 |
74 | |
75 | /* Types */ |
76 | #define SELECT_TYPE 0 |
77 | #define UPDATE_TYPE 1 |
78 | #define INSERT_TYPE 2 |
79 | #define UPDATE_TYPE_REQUIRES_PREFIX 3 |
80 | #define CREATE_TABLE_TYPE 4 |
81 | #define SELECT_TYPE_REQUIRES_PREFIX 5 |
82 | #define DELETE_TYPE_REQUIRES_PREFIX 6 |
83 | |
84 | #include "client_priv.h" |
85 | #include <mysqld_error.h> |
86 | #include <my_dir.h> |
87 | #include <signal.h> |
88 | #include <sslopt-vars.h> |
89 | #ifndef __WIN__ |
90 | #include <sys/wait.h> |
91 | #endif |
92 | #include <ctype.h> |
93 | #include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */ |
94 | |
95 | #ifdef __WIN__ |
96 | #define srandom srand |
97 | #define random rand |
98 | #define snprintf _snprintf |
99 | #endif |
100 | |
101 | #ifdef HAVE_SMEM |
102 | static char *shared_memory_base_name=0; |
103 | #endif |
104 | |
105 | /* Global Thread counter */ |
106 | uint thread_counter; |
107 | pthread_mutex_t counter_mutex; |
108 | pthread_cond_t count_threshhold; |
109 | uint master_wakeup; |
110 | pthread_mutex_t sleeper_mutex; |
111 | pthread_cond_t sleep_threshhold; |
112 | |
113 | static char **defaults_argv; |
114 | |
115 | char **primary_keys; |
116 | unsigned long long primary_keys_number_of; |
117 | |
118 | static char *host= NULL, *opt_password= NULL, *user= NULL, |
119 | *user_supplied_query= NULL, |
120 | *user_supplied_pre_statements= NULL, |
121 | *user_supplied_post_statements= NULL, |
122 | *default_engine= NULL, |
123 | *pre_system= NULL, |
124 | *post_system= NULL, |
125 | *opt_mysql_unix_port= NULL, |
126 | *opt_init_command= NULL; |
127 | static char *opt_plugin_dir= 0, *opt_default_auth= 0; |
128 | |
129 | const char *delimiter= "\n" ; |
130 | |
131 | const char *create_schema_string= "mysqlslap" ; |
132 | |
133 | static my_bool opt_preserve= TRUE, opt_no_drop= FALSE; |
134 | static my_bool debug_info_flag= 0, debug_check_flag= 0; |
135 | static my_bool opt_only_print= FALSE; |
136 | static my_bool opt_compress= FALSE, tty_password= FALSE, |
137 | opt_silent= FALSE, |
138 | auto_generate_sql_autoincrement= FALSE, |
139 | auto_generate_sql_guid_primary= FALSE, |
140 | auto_generate_sql= FALSE; |
141 | const char *auto_generate_sql_type= "mixed" ; |
142 | |
143 | static unsigned long connect_flags= CLIENT_MULTI_RESULTS | |
144 | CLIENT_MULTI_STATEMENTS | |
145 | CLIENT_REMEMBER_OPTIONS; |
146 | |
147 | static int verbose; |
148 | static uint commit_rate; |
149 | static uint detach_rate; |
150 | const char *num_int_cols_opt; |
151 | const char *num_char_cols_opt; |
152 | |
153 | /* Yes, we do set defaults here */ |
154 | static unsigned int num_int_cols= 1; |
155 | static unsigned int num_char_cols= 1; |
156 | static unsigned int num_int_cols_index= 0; |
157 | static unsigned int num_char_cols_index= 0; |
158 | static unsigned int iterations; |
159 | static uint my_end_arg= 0; |
160 | static char *default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME; |
161 | static ulonglong actual_queries= 0; |
162 | static ulonglong auto_actual_queries; |
163 | static ulonglong auto_generate_sql_unique_write_number; |
164 | static ulonglong auto_generate_sql_unique_query_number; |
165 | static unsigned int auto_generate_sql_secondary_indexes; |
166 | static ulonglong num_of_query; |
167 | static ulonglong auto_generate_sql_number; |
168 | const char *concurrency_str= NULL; |
169 | static char *create_string; |
170 | uint *concurrency; |
171 | static char mysql_charsets_dir[FN_REFLEN+1]; |
172 | |
173 | const char *default_dbug_option="d:t:o,/tmp/mysqlslap.trace" ; |
174 | const char *opt_csv_str; |
175 | File csv_file; |
176 | |
177 | static uint opt_protocol= 0; |
178 | |
179 | static int get_options(int *argc,char ***argv); |
180 | static uint opt_mysql_port= 0; |
181 | |
182 | static const char *load_default_groups[]= |
183 | { "mysqlslap" , "client" , "client-server" , "client-mariadb" , 0 }; |
184 | |
185 | typedef struct statement statement; |
186 | |
187 | struct statement { |
188 | char *string; |
189 | size_t length; |
190 | unsigned char type; |
191 | char *option; |
192 | size_t option_length; |
193 | statement *next; |
194 | }; |
195 | |
196 | typedef struct option_string option_string; |
197 | |
198 | struct option_string { |
199 | char *string; |
200 | size_t length; |
201 | char *option; |
202 | size_t option_length; |
203 | option_string *next; |
204 | }; |
205 | |
206 | typedef struct stats stats; |
207 | |
208 | struct stats { |
209 | long int timing; |
210 | uint users; |
211 | unsigned long long rows; |
212 | }; |
213 | |
214 | typedef struct thread_context thread_context; |
215 | |
216 | struct thread_context { |
217 | statement *stmt; |
218 | ulonglong limit; |
219 | }; |
220 | |
221 | typedef struct conclusions conclusions; |
222 | |
223 | struct conclusions { |
224 | char *engine; |
225 | long int avg_timing; |
226 | long int max_timing; |
227 | long int min_timing; |
228 | uint users; |
229 | unsigned long long avg_rows; |
230 | /* The following are not used yet */ |
231 | unsigned long long max_rows; |
232 | unsigned long long min_rows; |
233 | }; |
234 | |
235 | static option_string *engine_options= NULL; |
236 | static statement *pre_statements= NULL; |
237 | static statement *post_statements= NULL; |
238 | static statement *create_statements= NULL, |
239 | *query_statements= NULL; |
240 | |
241 | /* Prototypes */ |
242 | void print_conclusions(conclusions *con); |
243 | void print_conclusions_csv(conclusions *con); |
244 | void generate_stats(conclusions *con, option_string *eng, stats *sptr); |
245 | uint parse_comma(const char *string, uint **range); |
246 | uint parse_delimiter(const char *script, statement **stmt, char delm); |
247 | int parse_option(const char *origin, option_string **stmt, char delm); |
248 | static int drop_schema(MYSQL *mysql, const char *db); |
249 | uint get_random_string(char *buf); |
250 | static statement *build_table_string(void); |
251 | static statement *build_insert_string(void); |
252 | static statement *build_update_string(void); |
253 | static statement * build_select_string(my_bool key); |
254 | static int generate_primary_key_list(MYSQL *mysql, option_string *engine_stmt); |
255 | static int drop_primary_key_list(void); |
256 | static int create_schema(MYSQL *mysql, const char *db, statement *stmt, |
257 | option_string *engine_stmt); |
258 | static int run_scheduler(stats *sptr, statement *stmts, uint concur, |
259 | ulonglong limit); |
260 | pthread_handler_t run_task(void *p); |
261 | void statement_cleanup(statement *stmt); |
262 | void option_cleanup(option_string *stmt); |
263 | void concurrency_loop(MYSQL *mysql, uint current, option_string *eptr); |
264 | static int run_statements(MYSQL *mysql, statement *stmt); |
265 | int slap_connect(MYSQL *mysql); |
266 | static int run_query(MYSQL *mysql, const char *query, size_t len); |
267 | |
268 | static const char ALPHANUMERICS[]= |
269 | "0123456789ABCDEFGHIJKLMNOPQRSTWXYZabcdefghijklmnopqrstuvwxyz" ; |
270 | |
271 | #define ALPHANUMERICS_SIZE (sizeof(ALPHANUMERICS)-1) |
272 | |
273 | |
274 | static long int timedif(struct timeval a, struct timeval b) |
275 | { |
276 | register int us, s; |
277 | |
278 | us = a.tv_usec - b.tv_usec; |
279 | us /= 1000; |
280 | s = a.tv_sec - b.tv_sec; |
281 | s *= 1000; |
282 | return s + us; |
283 | } |
284 | |
285 | #ifdef __WIN__ |
286 | static int gettimeofday(struct timeval *tp, void *tzp) |
287 | { |
288 | unsigned int ticks; |
289 | ticks= GetTickCount(); |
290 | tp->tv_usec= ticks*1000; |
291 | tp->tv_sec= ticks/1000; |
292 | |
293 | return 0; |
294 | } |
295 | #endif |
296 | |
297 | void set_mysql_connect_options(MYSQL *mysql) |
298 | { |
299 | if (opt_compress) |
300 | mysql_options(mysql,MYSQL_OPT_COMPRESS,NullS); |
301 | #ifdef HAVE_OPENSSL |
302 | if (opt_use_ssl) |
303 | { |
304 | mysql_ssl_set(mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, |
305 | opt_ssl_capath, opt_ssl_cipher); |
306 | mysql_options(mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl); |
307 | mysql_options(mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath); |
308 | } |
309 | #endif |
310 | if (opt_protocol) |
311 | mysql_options(mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); |
312 | #ifdef HAVE_SMEM |
313 | if (shared_memory_base_name) |
314 | mysql_options(mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name); |
315 | #endif |
316 | mysql_options(mysql, MYSQL_SET_CHARSET_NAME, default_charset); |
317 | } |
318 | |
319 | |
320 | int main(int argc, char **argv) |
321 | { |
322 | MYSQL mysql; |
323 | option_string *eptr; |
324 | |
325 | MY_INIT(argv[0]); |
326 | sf_leaking_memory=1; /* don't report memory leaks on early exits */ |
327 | |
328 | load_defaults_or_exit("my" , load_default_groups, &argc, &argv); |
329 | defaults_argv=argv; |
330 | if (get_options(&argc,&argv)) |
331 | { |
332 | free_defaults(defaults_argv); |
333 | my_end(0); |
334 | exit(1); |
335 | } |
336 | sf_leaking_memory=0; /* from now on we cleanup properly */ |
337 | |
338 | /* Seed the random number generator if we will be using it. */ |
339 | if (auto_generate_sql) |
340 | srandom((uint)time(NULL)); |
341 | |
342 | if (argc > 2) |
343 | { |
344 | fprintf(stderr,"%s: Too many arguments\n" ,my_progname); |
345 | free_defaults(defaults_argv); |
346 | my_end(0); |
347 | exit(1); |
348 | } |
349 | mysql_init(&mysql); |
350 | set_mysql_connect_options(&mysql); |
351 | |
352 | if (opt_plugin_dir && *opt_plugin_dir) |
353 | mysql_options(&mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir); |
354 | |
355 | if (opt_default_auth && *opt_default_auth) |
356 | mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth); |
357 | |
358 | mysql_options(&mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0); |
359 | mysql_options4(&mysql, MYSQL_OPT_CONNECT_ATTR_ADD, |
360 | "program_name" , "mysqlslap" ); |
361 | if (!opt_only_print) |
362 | { |
363 | if (!(mysql_real_connect(&mysql, host, user, opt_password, |
364 | NULL, opt_mysql_port, |
365 | opt_mysql_unix_port, connect_flags))) |
366 | { |
367 | fprintf(stderr,"%s: Error when connecting to server: %s\n" , |
368 | my_progname,mysql_error(&mysql)); |
369 | mysql_close(&mysql); |
370 | free_defaults(defaults_argv); |
371 | my_end(0); |
372 | exit(1); |
373 | } |
374 | } |
375 | |
376 | pthread_mutex_init(&counter_mutex, NULL); |
377 | pthread_cond_init(&count_threshhold, NULL); |
378 | pthread_mutex_init(&sleeper_mutex, NULL); |
379 | pthread_cond_init(&sleep_threshhold, NULL); |
380 | |
381 | /* Main iterations loop */ |
382 | eptr= engine_options; |
383 | do |
384 | { |
385 | /* For the final stage we run whatever queries we were asked to run */ |
386 | uint *current; |
387 | |
388 | if (verbose >= 2) |
389 | printf("Starting Concurrency Test\n" ); |
390 | |
391 | if (*concurrency) |
392 | { |
393 | for (current= concurrency; current && *current; current++) |
394 | concurrency_loop(&mysql, *current, eptr); |
395 | } |
396 | else |
397 | { |
398 | uint infinite= 1; |
399 | do { |
400 | concurrency_loop(&mysql, infinite, eptr); |
401 | } |
402 | while (infinite++); |
403 | } |
404 | |
405 | if (!opt_preserve) |
406 | drop_schema(&mysql, create_schema_string); |
407 | |
408 | } while (eptr ? (eptr= eptr->next) : 0); |
409 | |
410 | pthread_mutex_destroy(&counter_mutex); |
411 | pthread_cond_destroy(&count_threshhold); |
412 | pthread_mutex_destroy(&sleeper_mutex); |
413 | pthread_cond_destroy(&sleep_threshhold); |
414 | |
415 | mysql_close(&mysql); /* Close & free connection */ |
416 | |
417 | /* now free all the strings we created */ |
418 | my_free(opt_password); |
419 | my_free(concurrency); |
420 | |
421 | statement_cleanup(create_statements); |
422 | statement_cleanup(query_statements); |
423 | statement_cleanup(pre_statements); |
424 | statement_cleanup(post_statements); |
425 | option_cleanup(engine_options); |
426 | |
427 | #ifdef HAVE_SMEM |
428 | my_free(shared_memory_base_name); |
429 | #endif |
430 | free_defaults(defaults_argv); |
431 | mysql_library_end(); |
432 | my_end(my_end_arg); |
433 | |
434 | return 0; |
435 | } |
436 | |
437 | void concurrency_loop(MYSQL *mysql, uint current, option_string *eptr) |
438 | { |
439 | unsigned int x; |
440 | stats *head_sptr; |
441 | stats *sptr; |
442 | conclusions conclusion; |
443 | unsigned long long client_limit; |
444 | int sysret; |
445 | |
446 | head_sptr= (stats *)my_malloc(sizeof(stats) * iterations, |
447 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
448 | |
449 | bzero(&conclusion, sizeof(conclusions)); |
450 | |
451 | if (auto_actual_queries) |
452 | client_limit= auto_actual_queries; |
453 | else if (num_of_query) |
454 | client_limit= num_of_query / current; |
455 | else |
456 | client_limit= actual_queries; |
457 | |
458 | for (x= 0, sptr= head_sptr; x < iterations; x++, sptr++) |
459 | { |
460 | /* |
461 | We might not want to load any data, such as when we are calling |
462 | a stored_procedure that doesn't use data, or we know we already have |
463 | data in the table. |
464 | */ |
465 | if (!opt_preserve) |
466 | drop_schema(mysql, create_schema_string); |
467 | |
468 | /* First we create */ |
469 | if (create_statements) |
470 | { |
471 | /* |
472 | If we have an --engine option, then we indicate |
473 | create_schema() to add the engine type to the DDL. |
474 | */ |
475 | if (eptr) |
476 | create_statements->type= CREATE_TABLE_TYPE; |
477 | |
478 | create_schema(mysql, create_schema_string, create_statements, eptr); |
479 | } |
480 | |
481 | /* |
482 | If we generated GUID we need to build a list of them from creation that |
483 | we can later use. |
484 | */ |
485 | if (verbose >= 2) |
486 | printf("Generating primary key list\n" ); |
487 | if (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary) |
488 | generate_primary_key_list(mysql, eptr); |
489 | |
490 | if (commit_rate) |
491 | run_query(mysql, "SET AUTOCOMMIT=0" , strlen("SET AUTOCOMMIT=0" )); |
492 | |
493 | if (pre_system && (sysret= system(pre_system)) != 0) |
494 | fprintf(stderr, |
495 | "Warning: Execution of pre_system option returned %d.\n" , |
496 | sysret); |
497 | |
498 | /* |
499 | Pre statements are always run after all other logic so they can |
500 | correct/adjust any item that they want. |
501 | */ |
502 | if (pre_statements) |
503 | run_statements(mysql, pre_statements); |
504 | |
505 | run_scheduler(sptr, query_statements, current, client_limit); |
506 | |
507 | if (post_statements) |
508 | run_statements(mysql, post_statements); |
509 | |
510 | if (post_system && (sysret= system(post_system)) != 0) |
511 | fprintf(stderr, |
512 | "Warning: Execution of post_system option returned %d.\n" , |
513 | sysret); |
514 | /* We are finished with this run */ |
515 | if (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary) |
516 | drop_primary_key_list(); |
517 | } |
518 | |
519 | if (verbose >= 2) |
520 | printf("Generating stats\n" ); |
521 | |
522 | generate_stats(&conclusion, eptr, head_sptr); |
523 | |
524 | if (!opt_silent) |
525 | print_conclusions(&conclusion); |
526 | if (opt_csv_str) |
527 | print_conclusions_csv(&conclusion); |
528 | |
529 | my_free(head_sptr); |
530 | |
531 | } |
532 | |
533 | |
534 | static struct my_option my_long_options[] = |
535 | { |
536 | {"help" , '?', "Display this help and exit." , 0, 0, 0, GET_NO_ARG, NO_ARG, |
537 | 0, 0, 0, 0, 0, 0}, |
538 | {"auto-generate-sql" , 'a', |
539 | "Generate SQL where not supplied by file or command line." , |
540 | &auto_generate_sql, &auto_generate_sql, |
541 | 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
542 | {"auto-generate-sql-add-autoincrement" , OPT_SLAP_AUTO_GENERATE_ADD_AUTO, |
543 | "Add an AUTO_INCREMENT column to auto-generated tables." , |
544 | &auto_generate_sql_autoincrement, |
545 | &auto_generate_sql_autoincrement, |
546 | 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
547 | {"auto-generate-sql-execute-number" , OPT_SLAP_AUTO_GENERATE_EXECUTE_QUERIES, |
548 | "Set this number to generate a set number of queries to run." , |
549 | &auto_actual_queries, &auto_actual_queries, |
550 | 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
551 | {"auto-generate-sql-guid-primary" , OPT_SLAP_AUTO_GENERATE_GUID_PRIMARY, |
552 | "Add GUID based primary keys to auto-generated tables." , |
553 | &auto_generate_sql_guid_primary, |
554 | &auto_generate_sql_guid_primary, |
555 | 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
556 | {"auto-generate-sql-load-type" , OPT_SLAP_AUTO_GENERATE_SQL_LOAD_TYPE, |
557 | "Specify test load type: mixed, update, write, key, or read; default is mixed." , |
558 | (char**) &auto_generate_sql_type, (char**) &auto_generate_sql_type, |
559 | 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
560 | {"auto-generate-sql-secondary-indexes" , |
561 | OPT_SLAP_AUTO_GENERATE_SECONDARY_INDEXES, |
562 | "Number of secondary indexes to add to auto-generated tables." , |
563 | &auto_generate_sql_secondary_indexes, |
564 | &auto_generate_sql_secondary_indexes, 0, |
565 | GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
566 | {"auto-generate-sql-unique-query-number" , |
567 | OPT_SLAP_AUTO_GENERATE_UNIQUE_QUERY_NUM, |
568 | "Number of unique queries to generate for automatic tests." , |
569 | &auto_generate_sql_unique_query_number, |
570 | &auto_generate_sql_unique_query_number, |
571 | 0, GET_ULL, REQUIRED_ARG, 10, 0, 0, 0, 0, 0}, |
572 | {"auto-generate-sql-unique-write-number" , |
573 | OPT_SLAP_AUTO_GENERATE_UNIQUE_WRITE_NUM, |
574 | "Number of unique queries to generate for auto-generate-sql-write-number." , |
575 | &auto_generate_sql_unique_write_number, |
576 | &auto_generate_sql_unique_write_number, |
577 | 0, GET_ULL, REQUIRED_ARG, 10, 0, 0, 0, 0, 0}, |
578 | {"auto-generate-sql-write-number" , OPT_SLAP_AUTO_GENERATE_WRITE_NUM, |
579 | "Number of row inserts to perform for each thread (default is 100)." , |
580 | &auto_generate_sql_number, &auto_generate_sql_number, |
581 | 0, GET_ULL, REQUIRED_ARG, 100, 0, 0, 0, 0, 0}, |
582 | {"character-sets-dir" , OPT_CHARSETS_DIR, |
583 | "Directory for character set files." , (char **)&charsets_dir, |
584 | (char **)&charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
585 | {"commit" , OPT_SLAP_COMMIT, "Commit records every X number of statements." , |
586 | &commit_rate, &commit_rate, 0, GET_UINT, REQUIRED_ARG, |
587 | 0, 0, 0, 0, 0, 0}, |
588 | {"compress" , 'C', "Use compression in server/client protocol." , |
589 | &opt_compress, &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0, |
590 | 0, 0, 0}, |
591 | {"concurrency" , 'c', "Number of clients to simulate for query to run." , |
592 | (char**) &concurrency_str, (char**) &concurrency_str, 0, GET_STR, |
593 | REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
594 | {"create" , OPT_SLAP_CREATE_STRING, "File or string to use create tables." , |
595 | &create_string, &create_string, 0, GET_STR, REQUIRED_ARG, |
596 | 0, 0, 0, 0, 0, 0}, |
597 | {"create-schema" , OPT_CREATE_SLAP_SCHEMA, "Schema to run tests in." , |
598 | (char**) &create_schema_string, (char**) &create_schema_string, 0, GET_STR, |
599 | REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
600 | {"csv" , OPT_SLAP_CSV, |
601 | "Generate CSV output to named file or to stdout if no file is named." , |
602 | NULL, NULL, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, |
603 | #ifdef DBUG_OFF |
604 | {"debug" , '#', "This is a non-debug version. Catch this and exit." , |
605 | 0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, |
606 | #else |
607 | {"debug" , '#', "Output debug log. Often this is 'd:t:o,filename'." , |
608 | (char**) &default_dbug_option, (char**) &default_dbug_option, 0, GET_STR, |
609 | OPT_ARG, 0, 0, 0, 0, 0, 0}, |
610 | #endif |
611 | {"debug-check" , OPT_DEBUG_CHECK, "Check memory and open file usage at exit." , |
612 | &debug_check_flag, &debug_check_flag, 0, |
613 | GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
614 | {"debug-info" , 'T', "Print some debug info at exit." , &debug_info_flag, |
615 | &debug_info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
616 | {"default_auth" , OPT_DEFAULT_AUTH, |
617 | "Default authentication client-side plugin to use." , |
618 | &opt_default_auth, &opt_default_auth, 0, |
619 | GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
620 | {"delimiter" , 'F', |
621 | "Delimiter to use in SQL statements supplied in file or command line." , |
622 | (char**) &delimiter, (char**) &delimiter, 0, GET_STR, REQUIRED_ARG, |
623 | 0, 0, 0, 0, 0, 0}, |
624 | {"detach" , OPT_SLAP_DETACH, |
625 | "Detach (close and reopen) connections after X number of requests." , |
626 | &detach_rate, &detach_rate, 0, GET_UINT, REQUIRED_ARG, |
627 | 0, 0, 0, 0, 0, 0}, |
628 | {"engine" , 'e', |
629 | "Comma separated list of storage engines to use for creating the table." |
630 | " The test is run for each engine. You can also specify an option for an " |
631 | "engine after a `:', like memory:max_row=2300" , |
632 | &default_engine, &default_engine, 0, |
633 | GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
634 | {"host" , 'h', "Connect to host." , &host, &host, 0, GET_STR, |
635 | REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
636 | {"init-command" , OPT_INIT_COMMAND, |
637 | "SQL Command to execute when connecting to MySQL server. Will " |
638 | "automatically be re-executed when reconnecting." , |
639 | &opt_init_command, &opt_init_command, 0, |
640 | GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
641 | {"iterations" , 'i', "Number of times to run the tests." , &iterations, |
642 | &iterations, 0, GET_UINT, REQUIRED_ARG, 1, 0, 0, 0, 0, 0}, |
643 | {"no-drop" , OPT_SLAP_NO_DROP, "Do not drop the schema after the test." , |
644 | &opt_no_drop, &opt_no_drop, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
645 | {"number-char-cols" , 'x', |
646 | "Number of VARCHAR columns to create in table if specifying --auto-generate-sql." , |
647 | (char**) &num_char_cols_opt, (char**) &num_char_cols_opt, 0, GET_STR, REQUIRED_ARG, |
648 | 0, 0, 0, 0, 0, 0}, |
649 | {"number-int-cols" , 'y', |
650 | "Number of INT columns to create in table if specifying --auto-generate-sql." , |
651 | (char**) &num_int_cols_opt, (char**) &num_int_cols_opt, 0, GET_STR, REQUIRED_ARG, |
652 | 0, 0, 0, 0, 0, 0}, |
653 | {"number-of-queries" , OPT_MYSQL_NUMBER_OF_QUERY, |
654 | "Limit each client to this number of queries (this is not exact)." , |
655 | &num_of_query, &num_of_query, 0, |
656 | GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
657 | {"only-print" , OPT_MYSQL_ONLY_PRINT, |
658 | "Do not connect to the databases, but instead print out what would have " |
659 | "been done." , |
660 | &opt_only_print, &opt_only_print, 0, GET_BOOL, NO_ARG, |
661 | 0, 0, 0, 0, 0, 0}, |
662 | {"password" , 'p', |
663 | "Password to use when connecting to server. If password is not given it's " |
664 | "asked from the tty." , 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, |
665 | #ifdef __WIN__ |
666 | {"pipe" , 'W', "Use named pipes to connect to server." , 0, 0, 0, GET_NO_ARG, |
667 | NO_ARG, 0, 0, 0, 0, 0, 0}, |
668 | #endif |
669 | {"plugin_dir" , OPT_PLUGIN_DIR, "Directory for client-side plugins." , |
670 | &opt_plugin_dir, &opt_plugin_dir, 0, |
671 | GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
672 | {"port" , 'P', "Port number to use for connection." , &opt_mysql_port, |
673 | &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0, |
674 | 0}, |
675 | {"post-query" , OPT_SLAP_POST_QUERY, |
676 | "Query to run or file containing query to execute after tests have completed." , |
677 | &user_supplied_post_statements, &user_supplied_post_statements, |
678 | 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
679 | {"post-system" , OPT_SLAP_POST_SYSTEM, |
680 | "system() string to execute after tests have completed." , |
681 | &post_system, &post_system, |
682 | 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
683 | {"pre-query" , OPT_SLAP_PRE_QUERY, |
684 | "Query to run or file containing query to execute before running tests." , |
685 | &user_supplied_pre_statements, &user_supplied_pre_statements, |
686 | 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
687 | {"pre-system" , OPT_SLAP_PRE_SYSTEM, |
688 | "system() string to execute before running tests." , |
689 | &pre_system, &pre_system, |
690 | 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
691 | {"protocol" , OPT_MYSQL_PROTOCOL, |
692 | "The protocol to use for connection (tcp, socket, pipe, memory)." , |
693 | 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
694 | {"query" , 'q', "Query to run or file containing query to run." , |
695 | &user_supplied_query, &user_supplied_query, |
696 | 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
697 | #ifdef HAVE_SMEM |
698 | {"shared-memory-base-name" , OPT_SHARED_MEMORY_BASE_NAME, |
699 | "Base name of shared memory." , &shared_memory_base_name, |
700 | &shared_memory_base_name, 0, GET_STR_ALLOC, REQUIRED_ARG, |
701 | 0, 0, 0, 0, 0, 0}, |
702 | #endif |
703 | {"silent" , 's', "Run program in silent mode - no output." , |
704 | &opt_silent, &opt_silent, 0, GET_BOOL, NO_ARG, |
705 | 0, 0, 0, 0, 0, 0}, |
706 | {"socket" , 'S', "The socket file to use for connection." , |
707 | &opt_mysql_unix_port, &opt_mysql_unix_port, 0, GET_STR, |
708 | REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
709 | #include <sslopt-longopts.h> |
710 | #ifndef DONT_ALLOW_USER_CHANGE |
711 | {"user" , 'u', "User for login if not current user." , &user, |
712 | &user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
713 | #endif |
714 | {"verbose" , 'v', |
715 | "More verbose output; you can use this multiple times to get even more " |
716 | "verbose output." , &verbose, &verbose, 0, GET_NO_ARG, NO_ARG, |
717 | 0, 0, 0, 0, 0, 0}, |
718 | {"version" , 'V', "Output version information and exit." , 0, 0, 0, |
719 | GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, |
720 | {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} |
721 | }; |
722 | |
723 | |
724 | static void print_version(void) |
725 | { |
726 | printf("%s Ver %s Distrib %s, for %s (%s)\n" ,my_progname, SLAP_VERSION, |
727 | MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE); |
728 | } |
729 | |
730 | |
731 | static void usage(void) |
732 | { |
733 | print_version(); |
734 | puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2005" )); |
735 | puts("Run a query multiple times against the server.\n" ); |
736 | printf("Usage: %s [OPTIONS]\n" ,my_progname); |
737 | print_defaults("my" ,load_default_groups); |
738 | puts("" ); |
739 | my_print_help(my_long_options); |
740 | my_print_variables(my_long_options); |
741 | } |
742 | |
743 | |
744 | static my_bool |
745 | get_one_option(int optid, const struct my_option *opt __attribute__((unused)), |
746 | char *argument) |
747 | { |
748 | DBUG_ENTER("get_one_option" ); |
749 | switch(optid) { |
750 | case 'v': |
751 | verbose++; |
752 | break; |
753 | case 'p': |
754 | if (argument == disabled_my_option) |
755 | argument= (char*) "" ; /* Don't require password */ |
756 | if (argument) |
757 | { |
758 | char *start= argument; |
759 | my_free(opt_password); |
760 | opt_password= my_strdup(argument,MYF(MY_FAE)); |
761 | while (*argument) *argument++= 'x'; /* Destroy argument */ |
762 | if (*start) |
763 | start[1]= 0; /* Cut length of argument */ |
764 | tty_password= 0; |
765 | } |
766 | else |
767 | tty_password= 1; |
768 | break; |
769 | case 'W': |
770 | #ifdef __WIN__ |
771 | opt_protocol= MYSQL_PROTOCOL_PIPE; |
772 | #endif |
773 | break; |
774 | case OPT_MYSQL_PROTOCOL: |
775 | if ((opt_protocol= find_type_with_warning(argument, &sql_protocol_typelib, |
776 | opt->name)) <= 0) |
777 | { |
778 | sf_leaking_memory= 1; /* no memory leak reports here */ |
779 | exit(1); |
780 | } |
781 | break; |
782 | case '#': |
783 | DBUG_PUSH(argument ? argument : default_dbug_option); |
784 | debug_check_flag= 1; |
785 | break; |
786 | case OPT_CHARSETS_DIR: |
787 | strmake_buf(mysql_charsets_dir, argument); |
788 | charsets_dir = mysql_charsets_dir; |
789 | break; |
790 | case OPT_SLAP_CSV: |
791 | if (!argument) |
792 | argument= (char *)"-" ; /* use stdout */ |
793 | opt_csv_str= argument; |
794 | break; |
795 | #include <sslopt-case.h> |
796 | case 'V': |
797 | print_version(); |
798 | exit(0); |
799 | break; |
800 | case '?': |
801 | case 'I': /* Info */ |
802 | usage(); |
803 | exit(0); |
804 | } |
805 | DBUG_RETURN(0); |
806 | } |
807 | |
808 | |
809 | uint |
810 | get_random_string(char *buf) |
811 | { |
812 | char *buf_ptr= buf; |
813 | int x; |
814 | DBUG_ENTER("get_random_string" ); |
815 | for (x= RAND_STRING_SIZE; x > 0; x--) |
816 | *buf_ptr++= ALPHANUMERICS[random() % ALPHANUMERICS_SIZE]; |
817 | DBUG_RETURN((uint)(buf_ptr - buf)); |
818 | } |
819 | |
820 | |
821 | /* |
822 | build_table_string |
823 | |
824 | This function builds a create table query if the user opts to not supply |
825 | a file or string containing a create table statement |
826 | */ |
827 | static statement * |
828 | build_table_string(void) |
829 | { |
830 | char buf[HUGE_STRING_LENGTH]; |
831 | unsigned int col_count; |
832 | statement *ptr; |
833 | DYNAMIC_STRING table_string; |
834 | DBUG_ENTER("build_table_string" ); |
835 | |
836 | DBUG_PRINT("info" , ("num int cols %u num char cols %u" , |
837 | num_int_cols, num_char_cols)); |
838 | |
839 | init_dynamic_string(&table_string, "" , 1024, 1024); |
840 | |
841 | dynstr_append(&table_string, "CREATE TABLE `t1` (" ); |
842 | |
843 | if (auto_generate_sql_autoincrement) |
844 | { |
845 | dynstr_append(&table_string, "id serial" ); |
846 | |
847 | if (num_int_cols || num_char_cols) |
848 | dynstr_append(&table_string, "," ); |
849 | } |
850 | |
851 | if (auto_generate_sql_guid_primary) |
852 | { |
853 | dynstr_append(&table_string, "id varchar(32) primary key" ); |
854 | |
855 | if (num_int_cols || num_char_cols || auto_generate_sql_guid_primary) |
856 | dynstr_append(&table_string, "," ); |
857 | } |
858 | |
859 | if (auto_generate_sql_secondary_indexes) |
860 | { |
861 | unsigned int count; |
862 | |
863 | for (count= 0; count < auto_generate_sql_secondary_indexes; count++) |
864 | { |
865 | if (count) /* Except for the first pass we add a comma */ |
866 | dynstr_append(&table_string, "," ); |
867 | |
868 | if (snprintf(buf, HUGE_STRING_LENGTH, "id%d varchar(32) unique key" , count) |
869 | > HUGE_STRING_LENGTH) |
870 | { |
871 | fprintf(stderr, "Memory Allocation error in create table\n" ); |
872 | exit(1); |
873 | } |
874 | dynstr_append(&table_string, buf); |
875 | } |
876 | |
877 | if (num_int_cols || num_char_cols) |
878 | dynstr_append(&table_string, "," ); |
879 | } |
880 | |
881 | if (num_int_cols) |
882 | for (col_count= 1; col_count <= num_int_cols; col_count++) |
883 | { |
884 | if (num_int_cols_index) |
885 | { |
886 | if (snprintf(buf, HUGE_STRING_LENGTH, "intcol%d INT(32), INDEX(intcol%d)" , |
887 | col_count, col_count) > HUGE_STRING_LENGTH) |
888 | { |
889 | fprintf(stderr, "Memory Allocation error in create table\n" ); |
890 | exit(1); |
891 | } |
892 | } |
893 | else |
894 | { |
895 | if (snprintf(buf, HUGE_STRING_LENGTH, "intcol%d INT(32) " , col_count) |
896 | > HUGE_STRING_LENGTH) |
897 | { |
898 | fprintf(stderr, "Memory Allocation error in create table\n" ); |
899 | exit(1); |
900 | } |
901 | } |
902 | dynstr_append(&table_string, buf); |
903 | |
904 | if (col_count < num_int_cols || num_char_cols > 0) |
905 | dynstr_append(&table_string, "," ); |
906 | } |
907 | |
908 | if (num_char_cols) |
909 | for (col_count= 1; col_count <= num_char_cols; col_count++) |
910 | { |
911 | if (num_char_cols_index) |
912 | { |
913 | if (snprintf(buf, HUGE_STRING_LENGTH, |
914 | "charcol%d VARCHAR(128), INDEX(charcol%d) " , |
915 | col_count, col_count) > HUGE_STRING_LENGTH) |
916 | { |
917 | fprintf(stderr, "Memory Allocation error in creating table\n" ); |
918 | exit(1); |
919 | } |
920 | } |
921 | else |
922 | { |
923 | if (snprintf(buf, HUGE_STRING_LENGTH, "charcol%d VARCHAR(128)" , |
924 | col_count) > HUGE_STRING_LENGTH) |
925 | { |
926 | fprintf(stderr, "Memory Allocation error in creating table\n" ); |
927 | exit(1); |
928 | } |
929 | } |
930 | dynstr_append(&table_string, buf); |
931 | |
932 | if (col_count < num_char_cols) |
933 | dynstr_append(&table_string, "," ); |
934 | } |
935 | |
936 | dynstr_append(&table_string, ")" ); |
937 | ptr= (statement *)my_malloc(sizeof(statement), |
938 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
939 | ptr->string = (char *)my_malloc(table_string.length+1, |
940 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
941 | ptr->length= table_string.length+1; |
942 | ptr->type= CREATE_TABLE_TYPE; |
943 | strmov(ptr->string, table_string.str); |
944 | dynstr_free(&table_string); |
945 | DBUG_RETURN(ptr); |
946 | } |
947 | |
948 | /* |
949 | build_update_string() |
950 | |
951 | This function builds insert statements when the user opts to not supply |
952 | an insert file or string containing insert data |
953 | */ |
954 | static statement * |
955 | build_update_string(void) |
956 | { |
957 | char buf[HUGE_STRING_LENGTH]; |
958 | unsigned int col_count; |
959 | statement *ptr; |
960 | DYNAMIC_STRING update_string; |
961 | DBUG_ENTER("build_update_string" ); |
962 | |
963 | init_dynamic_string(&update_string, "" , 1024, 1024); |
964 | |
965 | dynstr_append(&update_string, "UPDATE t1 SET " ); |
966 | |
967 | if (num_int_cols) |
968 | for (col_count= 1; col_count <= num_int_cols; col_count++) |
969 | { |
970 | if (snprintf(buf, HUGE_STRING_LENGTH, "intcol%d = %ld" , col_count, |
971 | random()) > HUGE_STRING_LENGTH) |
972 | { |
973 | fprintf(stderr, "Memory Allocation error in creating update\n" ); |
974 | exit(1); |
975 | } |
976 | dynstr_append(&update_string, buf); |
977 | |
978 | if (col_count < num_int_cols || num_char_cols > 0) |
979 | dynstr_append_mem(&update_string, "," , 1); |
980 | } |
981 | |
982 | if (num_char_cols) |
983 | for (col_count= 1; col_count <= num_char_cols; col_count++) |
984 | { |
985 | char rand_buffer[RAND_STRING_SIZE]; |
986 | int buf_len= get_random_string(rand_buffer); |
987 | |
988 | if (snprintf(buf, HUGE_STRING_LENGTH, "charcol%d = '%.*s'" , col_count, |
989 | buf_len, rand_buffer) |
990 | > HUGE_STRING_LENGTH) |
991 | { |
992 | fprintf(stderr, "Memory Allocation error in creating update\n" ); |
993 | exit(1); |
994 | } |
995 | dynstr_append(&update_string, buf); |
996 | |
997 | if (col_count < num_char_cols) |
998 | dynstr_append_mem(&update_string, "," , 1); |
999 | } |
1000 | |
1001 | if (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary) |
1002 | dynstr_append(&update_string, " WHERE id = " ); |
1003 | |
1004 | |
1005 | ptr= (statement *)my_malloc(sizeof(statement), |
1006 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
1007 | |
1008 | ptr->string= (char *)my_malloc(update_string.length + 1, |
1009 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
1010 | ptr->length= update_string.length+1; |
1011 | if (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary) |
1012 | ptr->type= UPDATE_TYPE_REQUIRES_PREFIX ; |
1013 | else |
1014 | ptr->type= UPDATE_TYPE; |
1015 | |
1016 | strmov(ptr->string, update_string.str); |
1017 | dynstr_free(&update_string); |
1018 | DBUG_RETURN(ptr); |
1019 | } |
1020 | |
1021 | |
1022 | /* |
1023 | build_insert_string() |
1024 | |
1025 | This function builds insert statements when the user opts to not supply |
1026 | an insert file or string containing insert data |
1027 | */ |
1028 | static statement * |
1029 | build_insert_string(void) |
1030 | { |
1031 | char buf[HUGE_STRING_LENGTH]; |
1032 | unsigned int col_count; |
1033 | statement *ptr; |
1034 | DYNAMIC_STRING insert_string; |
1035 | DBUG_ENTER("build_insert_string" ); |
1036 | |
1037 | init_dynamic_string(&insert_string, "" , 1024, 1024); |
1038 | |
1039 | dynstr_append(&insert_string, "INSERT INTO t1 VALUES (" ); |
1040 | |
1041 | if (auto_generate_sql_autoincrement) |
1042 | { |
1043 | dynstr_append(&insert_string, "NULL" ); |
1044 | |
1045 | if (num_int_cols || num_char_cols) |
1046 | dynstr_append(&insert_string, "," ); |
1047 | } |
1048 | |
1049 | if (auto_generate_sql_guid_primary) |
1050 | { |
1051 | dynstr_append(&insert_string, "uuid()" ); |
1052 | |
1053 | if (num_int_cols || num_char_cols) |
1054 | dynstr_append(&insert_string, "," ); |
1055 | } |
1056 | |
1057 | if (auto_generate_sql_secondary_indexes) |
1058 | { |
1059 | unsigned int count; |
1060 | |
1061 | for (count= 0; count < auto_generate_sql_secondary_indexes; count++) |
1062 | { |
1063 | if (count) /* Except for the first pass we add a comma */ |
1064 | dynstr_append(&insert_string, "," ); |
1065 | |
1066 | dynstr_append(&insert_string, "uuid()" ); |
1067 | } |
1068 | |
1069 | if (num_int_cols || num_char_cols) |
1070 | dynstr_append(&insert_string, "," ); |
1071 | } |
1072 | |
1073 | if (num_int_cols) |
1074 | for (col_count= 1; col_count <= num_int_cols; col_count++) |
1075 | { |
1076 | if (snprintf(buf, HUGE_STRING_LENGTH, "%ld" , random()) > HUGE_STRING_LENGTH) |
1077 | { |
1078 | fprintf(stderr, "Memory Allocation error in creating insert\n" ); |
1079 | exit(1); |
1080 | } |
1081 | dynstr_append(&insert_string, buf); |
1082 | |
1083 | if (col_count < num_int_cols || num_char_cols > 0) |
1084 | dynstr_append_mem(&insert_string, "," , 1); |
1085 | } |
1086 | |
1087 | if (num_char_cols) |
1088 | for (col_count= 1; col_count <= num_char_cols; col_count++) |
1089 | { |
1090 | int buf_len= get_random_string(buf); |
1091 | dynstr_append_mem(&insert_string, "'" , 1); |
1092 | dynstr_append_mem(&insert_string, buf, buf_len); |
1093 | dynstr_append_mem(&insert_string, "'" , 1); |
1094 | |
1095 | if (col_count < num_char_cols) |
1096 | dynstr_append_mem(&insert_string, "," , 1); |
1097 | } |
1098 | |
1099 | dynstr_append_mem(&insert_string, ")" , 1); |
1100 | |
1101 | ptr= (statement *)my_malloc(sizeof(statement), |
1102 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
1103 | ptr->string= (char *)my_malloc(insert_string.length + 1, |
1104 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
1105 | ptr->length= insert_string.length+1; |
1106 | ptr->type= INSERT_TYPE; |
1107 | strmov(ptr->string, insert_string.str); |
1108 | dynstr_free(&insert_string); |
1109 | DBUG_RETURN(ptr); |
1110 | } |
1111 | |
1112 | |
1113 | /* |
1114 | build_select_string() |
1115 | |
1116 | This function builds a query if the user opts to not supply a query |
1117 | statement or file containing a query statement |
1118 | */ |
1119 | static statement * |
1120 | build_select_string(my_bool key) |
1121 | { |
1122 | char buf[HUGE_STRING_LENGTH]; |
1123 | unsigned int col_count; |
1124 | statement *ptr; |
1125 | static DYNAMIC_STRING query_string; |
1126 | DBUG_ENTER("build_select_string" ); |
1127 | |
1128 | init_dynamic_string(&query_string, "" , 1024, 1024); |
1129 | |
1130 | dynstr_append_mem(&query_string, "SELECT " , 7); |
1131 | for (col_count= 1; col_count <= num_int_cols; col_count++) |
1132 | { |
1133 | if (snprintf(buf, HUGE_STRING_LENGTH, "intcol%d" , col_count) |
1134 | > HUGE_STRING_LENGTH) |
1135 | { |
1136 | fprintf(stderr, "Memory Allocation error in creating select\n" ); |
1137 | exit(1); |
1138 | } |
1139 | dynstr_append(&query_string, buf); |
1140 | |
1141 | if (col_count < num_int_cols || num_char_cols > 0) |
1142 | dynstr_append_mem(&query_string, "," , 1); |
1143 | |
1144 | } |
1145 | for (col_count= 1; col_count <= num_char_cols; col_count++) |
1146 | { |
1147 | if (snprintf(buf, HUGE_STRING_LENGTH, "charcol%d" , col_count) |
1148 | > HUGE_STRING_LENGTH) |
1149 | { |
1150 | fprintf(stderr, "Memory Allocation error in creating select\n" ); |
1151 | exit(1); |
1152 | } |
1153 | dynstr_append(&query_string, buf); |
1154 | |
1155 | if (col_count < num_char_cols) |
1156 | dynstr_append_mem(&query_string, "," , 1); |
1157 | |
1158 | } |
1159 | dynstr_append(&query_string, " FROM t1" ); |
1160 | |
1161 | if ((key) && |
1162 | (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary)) |
1163 | dynstr_append(&query_string, " WHERE id = " ); |
1164 | |
1165 | ptr= (statement *)my_malloc(sizeof(statement), |
1166 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
1167 | ptr->string= (char *)my_malloc(query_string.length + 1, |
1168 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
1169 | ptr->length= query_string.length+1; |
1170 | if ((key) && |
1171 | (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary)) |
1172 | ptr->type= SELECT_TYPE_REQUIRES_PREFIX; |
1173 | else |
1174 | ptr->type= SELECT_TYPE; |
1175 | |
1176 | strmov(ptr->string, query_string.str); |
1177 | dynstr_free(&query_string); |
1178 | DBUG_RETURN(ptr); |
1179 | } |
1180 | |
1181 | static int |
1182 | get_options(int *argc,char ***argv) |
1183 | { |
1184 | int ho_error; |
1185 | char *tmp_string; |
1186 | MY_STAT sbuf; /* Stat information for the data file */ |
1187 | |
1188 | DBUG_ENTER("get_options" ); |
1189 | if ((ho_error= handle_options(argc, argv, my_long_options, get_one_option))) |
1190 | exit(ho_error); |
1191 | if (debug_info_flag) |
1192 | my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO; |
1193 | if (debug_check_flag) |
1194 | my_end_arg= MY_CHECK_ERROR; |
1195 | |
1196 | if (!user) |
1197 | user= (char *)"root" ; |
1198 | |
1199 | /* |
1200 | If something is created and --no-drop is not specified, we drop the |
1201 | schema. |
1202 | */ |
1203 | if (!opt_no_drop && (create_string || auto_generate_sql)) |
1204 | opt_preserve= FALSE; |
1205 | |
1206 | if (auto_generate_sql && (create_string || user_supplied_query)) |
1207 | { |
1208 | fprintf(stderr, |
1209 | "%s: Can't use --auto-generate-sql when create and query strings are specified!\n" , |
1210 | my_progname); |
1211 | exit(1); |
1212 | } |
1213 | |
1214 | if (auto_generate_sql && auto_generate_sql_guid_primary && |
1215 | auto_generate_sql_autoincrement) |
1216 | { |
1217 | fprintf(stderr, |
1218 | "%s: Either auto-generate-sql-guid-primary or auto-generate-sql-add-autoincrement can be used!\n" , |
1219 | my_progname); |
1220 | exit(1); |
1221 | } |
1222 | |
1223 | /* |
1224 | We are testing to make sure that if someone specified a key search |
1225 | that we actually added a key! |
1226 | */ |
1227 | if (auto_generate_sql && auto_generate_sql_type[0] == 'k') |
1228 | if ( auto_generate_sql_autoincrement == FALSE && |
1229 | auto_generate_sql_guid_primary == FALSE) |
1230 | { |
1231 | fprintf(stderr, |
1232 | "%s: Can't perform key test without a primary key!\n" , |
1233 | my_progname); |
1234 | exit(1); |
1235 | } |
1236 | |
1237 | if (auto_generate_sql && num_of_query && auto_actual_queries) |
1238 | { |
1239 | fprintf(stderr, |
1240 | "%s: Either auto-generate-sql-execute-number or number-of-queries can be used!\n" , |
1241 | my_progname); |
1242 | exit(1); |
1243 | } |
1244 | |
1245 | parse_comma(concurrency_str ? concurrency_str : "1" , &concurrency); |
1246 | |
1247 | if (opt_csv_str) |
1248 | { |
1249 | opt_silent= TRUE; |
1250 | |
1251 | if (opt_csv_str[0] == '-') |
1252 | { |
1253 | csv_file= my_fileno(stdout); |
1254 | } |
1255 | else |
1256 | { |
1257 | if ((csv_file= my_open(opt_csv_str, O_CREAT|O_WRONLY|O_APPEND, MYF(0))) |
1258 | == -1) |
1259 | { |
1260 | fprintf(stderr,"%s: Could not open csv file: %sn\n" , |
1261 | my_progname, opt_csv_str); |
1262 | exit(1); |
1263 | } |
1264 | } |
1265 | } |
1266 | |
1267 | if (opt_only_print) |
1268 | opt_silent= TRUE; |
1269 | |
1270 | if (num_int_cols_opt) |
1271 | { |
1272 | option_string *str; |
1273 | if(parse_option(num_int_cols_opt, &str, ',') == -1) |
1274 | { |
1275 | fprintf(stderr, "Invalid value specified for the option " |
1276 | "'number-int-cols'\n" ); |
1277 | option_cleanup(str); |
1278 | return 1; |
1279 | } |
1280 | num_int_cols= atoi(str->string); |
1281 | if (str->option) |
1282 | num_int_cols_index= atoi(str->option); |
1283 | |
1284 | option_cleanup(str); |
1285 | } |
1286 | |
1287 | if (num_char_cols_opt) |
1288 | { |
1289 | option_string *str; |
1290 | if(parse_option(num_char_cols_opt, &str, ',') == -1) |
1291 | { |
1292 | fprintf(stderr, "Invalid value specified for the option " |
1293 | "'number-char-cols'\n" ); |
1294 | option_cleanup(str); |
1295 | return 1; |
1296 | } |
1297 | num_char_cols= atoi(str->string); |
1298 | if (str->option) |
1299 | num_char_cols_index= atoi(str->option); |
1300 | else |
1301 | num_char_cols_index= 0; |
1302 | |
1303 | option_cleanup(str); |
1304 | } |
1305 | |
1306 | |
1307 | if (auto_generate_sql) |
1308 | { |
1309 | unsigned long long x= 0; |
1310 | statement *ptr_statement; |
1311 | |
1312 | if (verbose >= 2) |
1313 | printf("Building Create Statements for Auto\n" ); |
1314 | |
1315 | create_statements= build_table_string(); |
1316 | /* |
1317 | Pre-populate table |
1318 | */ |
1319 | for (ptr_statement= create_statements, x= 0; |
1320 | x < auto_generate_sql_unique_write_number; |
1321 | x++, ptr_statement= ptr_statement->next) |
1322 | { |
1323 | ptr_statement->next= build_insert_string(); |
1324 | } |
1325 | |
1326 | if (verbose >= 2) |
1327 | printf("Building Query Statements for Auto\n" ); |
1328 | |
1329 | if (auto_generate_sql_type[0] == 'r') |
1330 | { |
1331 | if (verbose >= 2) |
1332 | printf("Generating SELECT Statements for Auto\n" ); |
1333 | |
1334 | query_statements= build_select_string(FALSE); |
1335 | for (ptr_statement= query_statements, x= 0; |
1336 | x < auto_generate_sql_unique_query_number; |
1337 | x++, ptr_statement= ptr_statement->next) |
1338 | { |
1339 | ptr_statement->next= build_select_string(FALSE); |
1340 | } |
1341 | } |
1342 | else if (auto_generate_sql_type[0] == 'k') |
1343 | { |
1344 | if (verbose >= 2) |
1345 | printf("Generating SELECT for keys Statements for Auto\n" ); |
1346 | |
1347 | query_statements= build_select_string(TRUE); |
1348 | for (ptr_statement= query_statements, x= 0; |
1349 | x < auto_generate_sql_unique_query_number; |
1350 | x++, ptr_statement= ptr_statement->next) |
1351 | { |
1352 | ptr_statement->next= build_select_string(TRUE); |
1353 | } |
1354 | } |
1355 | else if (auto_generate_sql_type[0] == 'w') |
1356 | { |
1357 | /* |
1358 | We generate a number of strings in case the engine is |
1359 | Archive (since strings which were identical one after another |
1360 | would be too easily optimized). |
1361 | */ |
1362 | if (verbose >= 2) |
1363 | printf("Generating INSERT Statements for Auto\n" ); |
1364 | query_statements= build_insert_string(); |
1365 | for (ptr_statement= query_statements, x= 0; |
1366 | x < auto_generate_sql_unique_query_number; |
1367 | x++, ptr_statement= ptr_statement->next) |
1368 | { |
1369 | ptr_statement->next= build_insert_string(); |
1370 | } |
1371 | } |
1372 | else if (auto_generate_sql_type[0] == 'u') |
1373 | { |
1374 | query_statements= build_update_string(); |
1375 | for (ptr_statement= query_statements, x= 0; |
1376 | x < auto_generate_sql_unique_query_number; |
1377 | x++, ptr_statement= ptr_statement->next) |
1378 | { |
1379 | ptr_statement->next= build_update_string(); |
1380 | } |
1381 | } |
1382 | else /* Mixed mode is default */ |
1383 | { |
1384 | int coin= 0; |
1385 | |
1386 | query_statements= build_insert_string(); |
1387 | /* |
1388 | This logic should be extended to do a more mixed load, |
1389 | at the moment it results in "every other". |
1390 | */ |
1391 | for (ptr_statement= query_statements, x= 0; |
1392 | x < auto_generate_sql_unique_query_number; |
1393 | x++, ptr_statement= ptr_statement->next) |
1394 | { |
1395 | if (coin) |
1396 | { |
1397 | ptr_statement->next= build_insert_string(); |
1398 | coin= 0; |
1399 | } |
1400 | else |
1401 | { |
1402 | ptr_statement->next= build_select_string(TRUE); |
1403 | coin= 1; |
1404 | } |
1405 | } |
1406 | } |
1407 | } |
1408 | else |
1409 | { |
1410 | if (create_string && my_stat(create_string, &sbuf, MYF(0))) |
1411 | { |
1412 | File data_file; |
1413 | if (!MY_S_ISREG(sbuf.st_mode)) |
1414 | { |
1415 | fprintf(stderr,"%s: Create file was not a regular file\n" , |
1416 | my_progname); |
1417 | exit(1); |
1418 | } |
1419 | if ((data_file= my_open(create_string, O_RDWR, MYF(0))) == -1) |
1420 | { |
1421 | fprintf(stderr,"%s: Could not open create file\n" , my_progname); |
1422 | exit(1); |
1423 | } |
1424 | tmp_string= (char *)my_malloc((size_t)sbuf.st_size + 1, |
1425 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
1426 | my_read(data_file, (uchar*) tmp_string, (size_t)sbuf.st_size, MYF(0)); |
1427 | tmp_string[sbuf.st_size]= '\0'; |
1428 | my_close(data_file,MYF(0)); |
1429 | parse_delimiter(tmp_string, &create_statements, delimiter[0]); |
1430 | my_free(tmp_string); |
1431 | } |
1432 | else if (create_string) |
1433 | { |
1434 | parse_delimiter(create_string, &create_statements, delimiter[0]); |
1435 | } |
1436 | |
1437 | if (user_supplied_query && my_stat(user_supplied_query, &sbuf, MYF(0))) |
1438 | { |
1439 | File data_file; |
1440 | if (!MY_S_ISREG(sbuf.st_mode)) |
1441 | { |
1442 | fprintf(stderr,"%s: User query supplied file was not a regular file\n" , |
1443 | my_progname); |
1444 | exit(1); |
1445 | } |
1446 | if ((data_file= my_open(user_supplied_query, O_RDWR, MYF(0))) == -1) |
1447 | { |
1448 | fprintf(stderr,"%s: Could not open query supplied file\n" , my_progname); |
1449 | exit(1); |
1450 | } |
1451 | tmp_string= (char *)my_malloc((size_t)sbuf.st_size + 1, |
1452 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
1453 | my_read(data_file, (uchar*) tmp_string, (size_t)sbuf.st_size, MYF(0)); |
1454 | tmp_string[sbuf.st_size]= '\0'; |
1455 | my_close(data_file,MYF(0)); |
1456 | if (user_supplied_query) |
1457 | actual_queries= parse_delimiter(tmp_string, &query_statements, |
1458 | delimiter[0]); |
1459 | my_free(tmp_string); |
1460 | } |
1461 | else if (user_supplied_query) |
1462 | { |
1463 | actual_queries= parse_delimiter(user_supplied_query, &query_statements, |
1464 | delimiter[0]); |
1465 | } |
1466 | } |
1467 | |
1468 | if (user_supplied_pre_statements && my_stat(user_supplied_pre_statements, &sbuf, MYF(0))) |
1469 | { |
1470 | File data_file; |
1471 | if (!MY_S_ISREG(sbuf.st_mode)) |
1472 | { |
1473 | fprintf(stderr,"%s: User query supplied file was not a regular file\n" , |
1474 | my_progname); |
1475 | exit(1); |
1476 | } |
1477 | if ((data_file= my_open(user_supplied_pre_statements, O_RDWR, MYF(0))) == -1) |
1478 | { |
1479 | fprintf(stderr,"%s: Could not open query supplied file\n" , my_progname); |
1480 | exit(1); |
1481 | } |
1482 | tmp_string= (char *)my_malloc((size_t)sbuf.st_size + 1, |
1483 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
1484 | my_read(data_file, (uchar*) tmp_string, (size_t)sbuf.st_size, MYF(0)); |
1485 | tmp_string[sbuf.st_size]= '\0'; |
1486 | my_close(data_file,MYF(0)); |
1487 | if (user_supplied_pre_statements) |
1488 | (void)parse_delimiter(tmp_string, &pre_statements, |
1489 | delimiter[0]); |
1490 | my_free(tmp_string); |
1491 | } |
1492 | else if (user_supplied_pre_statements) |
1493 | { |
1494 | (void)parse_delimiter(user_supplied_pre_statements, |
1495 | &pre_statements, |
1496 | delimiter[0]); |
1497 | } |
1498 | |
1499 | if (user_supplied_post_statements && my_stat(user_supplied_post_statements, &sbuf, MYF(0))) |
1500 | { |
1501 | File data_file; |
1502 | if (!MY_S_ISREG(sbuf.st_mode)) |
1503 | { |
1504 | fprintf(stderr,"%s: User query supplied file was not a regular file\n" , |
1505 | my_progname); |
1506 | exit(1); |
1507 | } |
1508 | if ((data_file= my_open(user_supplied_post_statements, O_RDWR, MYF(0))) == -1) |
1509 | { |
1510 | fprintf(stderr,"%s: Could not open query supplied file\n" , my_progname); |
1511 | exit(1); |
1512 | } |
1513 | tmp_string= (char *)my_malloc((size_t)sbuf.st_size + 1, |
1514 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
1515 | my_read(data_file, (uchar*) tmp_string, (size_t)sbuf.st_size, MYF(0)); |
1516 | tmp_string[sbuf.st_size]= '\0'; |
1517 | my_close(data_file,MYF(0)); |
1518 | if (user_supplied_post_statements) |
1519 | (void)parse_delimiter(tmp_string, &post_statements, |
1520 | delimiter[0]); |
1521 | my_free(tmp_string); |
1522 | } |
1523 | else if (user_supplied_post_statements) |
1524 | { |
1525 | (void)parse_delimiter(user_supplied_post_statements, &post_statements, |
1526 | delimiter[0]); |
1527 | } |
1528 | |
1529 | if (verbose >= 2) |
1530 | printf("Parsing engines to use.\n" ); |
1531 | |
1532 | if (default_engine) |
1533 | { |
1534 | if(parse_option(default_engine, &engine_options, ',') == -1) |
1535 | { |
1536 | fprintf(stderr, "Invalid value specified for the option 'engine'\n" ); |
1537 | return 1; |
1538 | } |
1539 | } |
1540 | |
1541 | if (tty_password) |
1542 | opt_password= get_tty_password(NullS); |
1543 | |
1544 | DBUG_RETURN(0); |
1545 | } |
1546 | |
1547 | |
1548 | static int run_query(MYSQL *mysql, const char *query, size_t len) |
1549 | { |
1550 | if (opt_only_print) |
1551 | { |
1552 | printf("%.*s;\n" , (int)len, query); |
1553 | return 0; |
1554 | } |
1555 | |
1556 | if (verbose >= 3) |
1557 | printf("%.*s;\n" , (int)len, query); |
1558 | |
1559 | return mysql_real_query(mysql, query, (ulong)len); |
1560 | } |
1561 | |
1562 | |
1563 | static int |
1564 | generate_primary_key_list(MYSQL *mysql, option_string *engine_stmt) |
1565 | { |
1566 | MYSQL_RES *result; |
1567 | MYSQL_ROW row; |
1568 | unsigned long long counter; |
1569 | DBUG_ENTER("generate_primary_key_list" ); |
1570 | |
1571 | /* |
1572 | Blackhole is a special case, this allows us to test the upper end |
1573 | of the server during load runs. |
1574 | */ |
1575 | if (opt_only_print || (engine_stmt && |
1576 | strstr(engine_stmt->string, "blackhole" ))) |
1577 | { |
1578 | primary_keys_number_of= 1; |
1579 | primary_keys= (char **)my_malloc((uint)(sizeof(char *) * |
1580 | primary_keys_number_of), |
1581 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
1582 | /* Yes, we strdup a const string to simplify the interface */ |
1583 | primary_keys[0]= my_strdup("796c4422-1d94-102a-9d6d-00e0812d" , MYF(0)); |
1584 | } |
1585 | else |
1586 | { |
1587 | if (run_query(mysql, "SELECT id from t1" , strlen("SELECT id from t1" ))) |
1588 | { |
1589 | fprintf(stderr,"%s: Cannot select GUID primary keys. (%s)\n" , my_progname, |
1590 | mysql_error(mysql)); |
1591 | exit(1); |
1592 | } |
1593 | |
1594 | if (!(result= mysql_store_result(mysql))) |
1595 | { |
1596 | fprintf(stderr, "%s: Error when storing result: %d %s\n" , |
1597 | my_progname, mysql_errno(mysql), mysql_error(mysql)); |
1598 | exit(1); |
1599 | } |
1600 | primary_keys_number_of= mysql_num_rows(result); |
1601 | |
1602 | /* So why check this? Blackhole :) */ |
1603 | if (primary_keys_number_of) |
1604 | { |
1605 | /* |
1606 | We create the structure and loop and create the items. |
1607 | */ |
1608 | primary_keys= (char **)my_malloc((uint)(sizeof(char *) * |
1609 | primary_keys_number_of), |
1610 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
1611 | row= mysql_fetch_row(result); |
1612 | for (counter= 0; counter < primary_keys_number_of; |
1613 | counter++, row= mysql_fetch_row(result)) |
1614 | primary_keys[counter]= my_strdup(row[0], MYF(0)); |
1615 | } |
1616 | |
1617 | mysql_free_result(result); |
1618 | } |
1619 | |
1620 | DBUG_RETURN(0); |
1621 | } |
1622 | |
1623 | static int |
1624 | drop_primary_key_list(void) |
1625 | { |
1626 | unsigned long long counter; |
1627 | |
1628 | if (primary_keys_number_of) |
1629 | { |
1630 | for (counter= 0; counter < primary_keys_number_of; counter++) |
1631 | my_free(primary_keys[counter]); |
1632 | |
1633 | my_free(primary_keys); |
1634 | } |
1635 | |
1636 | return 0; |
1637 | } |
1638 | |
1639 | static int |
1640 | create_schema(MYSQL *mysql, const char *db, statement *stmt, |
1641 | option_string *engine_stmt) |
1642 | { |
1643 | char query[HUGE_STRING_LENGTH]; |
1644 | statement *ptr; |
1645 | statement *after_create; |
1646 | int len; |
1647 | ulonglong count; |
1648 | DBUG_ENTER("create_schema" ); |
1649 | |
1650 | len= snprintf(query, HUGE_STRING_LENGTH, "CREATE SCHEMA `%s`" , db); |
1651 | |
1652 | if (verbose >= 2) |
1653 | printf("Loading Pre-data\n" ); |
1654 | |
1655 | if (run_query(mysql, query, len)) |
1656 | { |
1657 | fprintf(stderr,"%s: Cannot create schema %s : %s\n" , my_progname, db, |
1658 | mysql_error(mysql)); |
1659 | exit(1); |
1660 | } |
1661 | |
1662 | if (opt_only_print) |
1663 | { |
1664 | printf("use %s;\n" , db); |
1665 | } |
1666 | else |
1667 | { |
1668 | if (verbose >= 3) |
1669 | printf("%s;\n" , query); |
1670 | |
1671 | if (mysql_select_db(mysql, db)) |
1672 | { |
1673 | fprintf(stderr,"%s: Cannot select schema '%s': %s\n" ,my_progname, db, |
1674 | mysql_error(mysql)); |
1675 | exit(1); |
1676 | } |
1677 | } |
1678 | |
1679 | count= 0; |
1680 | after_create= stmt; |
1681 | |
1682 | limit_not_met: |
1683 | for (ptr= after_create; ptr && ptr->length; ptr= ptr->next, count++) |
1684 | { |
1685 | if (auto_generate_sql && ( auto_generate_sql_number == count)) |
1686 | break; |
1687 | |
1688 | if (engine_stmt && engine_stmt->option && ptr->type == CREATE_TABLE_TYPE) |
1689 | { |
1690 | char buffer[HUGE_STRING_LENGTH]; |
1691 | |
1692 | snprintf(buffer, HUGE_STRING_LENGTH, "%s Engine = %s %s" , |
1693 | ptr->string, engine_stmt->string, engine_stmt->option); |
1694 | if (run_query(mysql, buffer, strlen(buffer))) |
1695 | { |
1696 | fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n" , |
1697 | my_progname, (uint)ptr->length, ptr->string, mysql_error(mysql)); |
1698 | exit(1); |
1699 | } |
1700 | } |
1701 | else if (engine_stmt && engine_stmt->string && ptr->type == CREATE_TABLE_TYPE) |
1702 | { |
1703 | char buffer[HUGE_STRING_LENGTH]; |
1704 | |
1705 | snprintf(buffer, HUGE_STRING_LENGTH, "%s Engine = %s" , |
1706 | ptr->string, engine_stmt->string); |
1707 | if (run_query(mysql, buffer, strlen(buffer))) |
1708 | { |
1709 | fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n" , |
1710 | my_progname, (uint)ptr->length, ptr->string, mysql_error(mysql)); |
1711 | exit(1); |
1712 | } |
1713 | } |
1714 | else |
1715 | { |
1716 | if (run_query(mysql, ptr->string, ptr->length)) |
1717 | { |
1718 | fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n" , |
1719 | my_progname, (uint)ptr->length, ptr->string, mysql_error(mysql)); |
1720 | exit(1); |
1721 | } |
1722 | } |
1723 | } |
1724 | |
1725 | if (auto_generate_sql && (auto_generate_sql_number > count )) |
1726 | { |
1727 | /* Special case for auto create, we don't want to create tables twice */ |
1728 | after_create= stmt->next; |
1729 | goto limit_not_met; |
1730 | } |
1731 | |
1732 | DBUG_RETURN(0); |
1733 | } |
1734 | |
1735 | static int |
1736 | drop_schema(MYSQL *mysql, const char *db) |
1737 | { |
1738 | char query[HUGE_STRING_LENGTH]; |
1739 | int len; |
1740 | |
1741 | DBUG_ENTER("drop_schema" ); |
1742 | len= snprintf(query, HUGE_STRING_LENGTH, "DROP SCHEMA IF EXISTS `%s`" , db); |
1743 | |
1744 | if (run_query(mysql, query, len)) |
1745 | { |
1746 | fprintf(stderr,"%s: Cannot drop database '%s' ERROR : %s\n" , |
1747 | my_progname, db, mysql_error(mysql)); |
1748 | exit(1); |
1749 | } |
1750 | |
1751 | |
1752 | |
1753 | DBUG_RETURN(0); |
1754 | } |
1755 | |
1756 | static int |
1757 | run_statements(MYSQL *mysql, statement *stmt) |
1758 | { |
1759 | statement *ptr; |
1760 | MYSQL_RES *result; |
1761 | DBUG_ENTER("run_statements" ); |
1762 | |
1763 | for (ptr= stmt; ptr && ptr->length; ptr= ptr->next) |
1764 | { |
1765 | if (run_query(mysql, ptr->string, ptr->length)) |
1766 | { |
1767 | fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n" , |
1768 | my_progname, (uint)ptr->length, ptr->string, mysql_error(mysql)); |
1769 | exit(1); |
1770 | } |
1771 | if (mysql_field_count(mysql)) |
1772 | { |
1773 | result= mysql_store_result(mysql); |
1774 | mysql_free_result(result); |
1775 | } |
1776 | } |
1777 | |
1778 | DBUG_RETURN(0); |
1779 | } |
1780 | |
1781 | static int |
1782 | run_scheduler(stats *sptr, statement *stmts, uint concur, ulonglong limit) |
1783 | { |
1784 | uint x; |
1785 | struct timeval start_time, end_time; |
1786 | thread_context con; |
1787 | pthread_t mainthread; /* Thread descriptor */ |
1788 | pthread_attr_t attr; /* Thread attributes */ |
1789 | DBUG_ENTER("run_scheduler" ); |
1790 | |
1791 | con.stmt= stmts; |
1792 | con.limit= limit; |
1793 | |
1794 | pthread_attr_init(&attr); |
1795 | pthread_attr_setdetachstate(&attr, |
1796 | PTHREAD_CREATE_DETACHED); |
1797 | |
1798 | pthread_mutex_lock(&counter_mutex); |
1799 | thread_counter= 0; |
1800 | |
1801 | pthread_mutex_lock(&sleeper_mutex); |
1802 | master_wakeup= 1; |
1803 | pthread_mutex_unlock(&sleeper_mutex); |
1804 | for (x= 0; x < concur; x++) |
1805 | { |
1806 | /* now you create the thread */ |
1807 | if (pthread_create(&mainthread, &attr, run_task, |
1808 | (void *)&con) != 0) |
1809 | { |
1810 | fprintf(stderr,"%s: Could not create thread\n" , |
1811 | my_progname); |
1812 | exit(0); |
1813 | } |
1814 | thread_counter++; |
1815 | } |
1816 | pthread_mutex_unlock(&counter_mutex); |
1817 | pthread_attr_destroy(&attr); |
1818 | |
1819 | pthread_mutex_lock(&sleeper_mutex); |
1820 | master_wakeup= 0; |
1821 | pthread_cond_broadcast(&sleep_threshhold); |
1822 | pthread_mutex_unlock(&sleeper_mutex); |
1823 | |
1824 | gettimeofday(&start_time, NULL); |
1825 | |
1826 | /* |
1827 | We loop until we know that all children have cleaned up. |
1828 | */ |
1829 | pthread_mutex_lock(&counter_mutex); |
1830 | while (thread_counter) |
1831 | { |
1832 | struct timespec abstime; |
1833 | |
1834 | set_timespec(abstime, 3); |
1835 | pthread_cond_timedwait(&count_threshhold, &counter_mutex, &abstime); |
1836 | } |
1837 | pthread_mutex_unlock(&counter_mutex); |
1838 | |
1839 | gettimeofday(&end_time, NULL); |
1840 | |
1841 | |
1842 | sptr->timing= timedif(end_time, start_time); |
1843 | sptr->users= concur; |
1844 | sptr->rows= limit; |
1845 | |
1846 | DBUG_RETURN(0); |
1847 | } |
1848 | |
1849 | |
1850 | pthread_handler_t run_task(void *p) |
1851 | { |
1852 | ulonglong counter= 0, queries; |
1853 | ulonglong detach_counter; |
1854 | unsigned int commit_counter; |
1855 | MYSQL *mysql; |
1856 | MYSQL_RES *result; |
1857 | MYSQL_ROW row; |
1858 | statement *ptr; |
1859 | thread_context *con= (thread_context *)p; |
1860 | |
1861 | DBUG_ENTER("run_task" ); |
1862 | DBUG_PRINT("info" , ("task script \"%s\"" , con->stmt ? con->stmt->string : "" )); |
1863 | |
1864 | pthread_mutex_lock(&sleeper_mutex); |
1865 | while (master_wakeup) |
1866 | { |
1867 | pthread_cond_wait(&sleep_threshhold, &sleeper_mutex); |
1868 | } |
1869 | pthread_mutex_unlock(&sleeper_mutex); |
1870 | |
1871 | if (mysql_thread_init()) |
1872 | { |
1873 | fprintf(stderr,"%s: mysql_thread_init() failed\n" , my_progname); |
1874 | exit(0); |
1875 | } |
1876 | |
1877 | if (!(mysql= mysql_init(NULL))) |
1878 | { |
1879 | fprintf(stderr,"%s: mysql_init() failed\n" , my_progname); |
1880 | mysql_thread_end(); |
1881 | exit(0); |
1882 | } |
1883 | |
1884 | set_mysql_connect_options(mysql); |
1885 | |
1886 | DBUG_PRINT("info" , ("trying to connect to host %s as user %s" , host, user)); |
1887 | |
1888 | if (!opt_only_print) |
1889 | { |
1890 | if (slap_connect(mysql)) |
1891 | goto end; |
1892 | } |
1893 | |
1894 | DBUG_PRINT("info" , ("connected." )); |
1895 | if (verbose >= 3) |
1896 | printf("connected!\n" ); |
1897 | queries= 0; |
1898 | |
1899 | commit_counter= 0; |
1900 | if (commit_rate) |
1901 | run_query(mysql, "SET AUTOCOMMIT=0" , strlen("SET AUTOCOMMIT=0" )); |
1902 | |
1903 | limit_not_met: |
1904 | for (ptr= con->stmt, detach_counter= 0; |
1905 | ptr && ptr->length; |
1906 | ptr= ptr->next, detach_counter++) |
1907 | { |
1908 | if (!opt_only_print && detach_rate && !(detach_counter % detach_rate)) |
1909 | { |
1910 | mysql_close(mysql); |
1911 | |
1912 | if (!(mysql= mysql_init(NULL))) |
1913 | { |
1914 | fprintf(stderr,"%s: mysql_init() failed ERROR : %s\n" , |
1915 | my_progname, mysql_error(mysql)); |
1916 | exit(0); |
1917 | } |
1918 | if (slap_connect(mysql)) |
1919 | goto end; |
1920 | } |
1921 | |
1922 | /* |
1923 | We have to execute differently based on query type. This should become a function. |
1924 | */ |
1925 | if ((ptr->type == UPDATE_TYPE_REQUIRES_PREFIX) || |
1926 | (ptr->type == SELECT_TYPE_REQUIRES_PREFIX)) |
1927 | { |
1928 | int length; |
1929 | unsigned int key_val; |
1930 | char *key; |
1931 | char buffer[HUGE_STRING_LENGTH]; |
1932 | |
1933 | /* |
1934 | This should only happen if some sort of new engine was |
1935 | implemented that didn't properly handle UPDATEs. |
1936 | |
1937 | Just in case someone runs this under an experimental engine we don't |
1938 | want a crash so the if() is placed here. |
1939 | */ |
1940 | DBUG_ASSERT(primary_keys_number_of); |
1941 | if (primary_keys_number_of) |
1942 | { |
1943 | key_val= (unsigned int)(random() % primary_keys_number_of); |
1944 | key= primary_keys[key_val]; |
1945 | |
1946 | DBUG_ASSERT(key); |
1947 | |
1948 | length= snprintf(buffer, HUGE_STRING_LENGTH, "%.*s '%s'" , |
1949 | (int)ptr->length, ptr->string, key); |
1950 | |
1951 | if (run_query(mysql, buffer, length)) |
1952 | { |
1953 | fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n" , |
1954 | my_progname, (uint)length, buffer, mysql_error(mysql)); |
1955 | exit(0); |
1956 | } |
1957 | } |
1958 | } |
1959 | else |
1960 | { |
1961 | if (run_query(mysql, ptr->string, ptr->length)) |
1962 | { |
1963 | fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n" , |
1964 | my_progname, (uint)ptr->length, ptr->string, mysql_error(mysql)); |
1965 | exit(0); |
1966 | } |
1967 | } |
1968 | |
1969 | do |
1970 | { |
1971 | if (mysql_field_count(mysql)) |
1972 | { |
1973 | if (!(result= mysql_store_result(mysql))) |
1974 | fprintf(stderr, "%s: Error when storing result: %d %s\n" , |
1975 | my_progname, mysql_errno(mysql), mysql_error(mysql)); |
1976 | else |
1977 | { |
1978 | while ((row= mysql_fetch_row(result))) |
1979 | counter++; |
1980 | mysql_free_result(result); |
1981 | } |
1982 | } |
1983 | } while(mysql_next_result(mysql) == 0); |
1984 | queries++; |
1985 | |
1986 | if (commit_rate && (++commit_counter == commit_rate)) |
1987 | { |
1988 | commit_counter= 0; |
1989 | run_query(mysql, "COMMIT" , strlen("COMMIT" )); |
1990 | } |
1991 | |
1992 | if (con->limit && queries == con->limit) |
1993 | goto end; |
1994 | } |
1995 | |
1996 | if (con->limit && queries < con->limit) |
1997 | goto limit_not_met; |
1998 | |
1999 | end: |
2000 | if (commit_rate) |
2001 | run_query(mysql, "COMMIT" , strlen("COMMIT" )); |
2002 | |
2003 | mysql_close(mysql); |
2004 | |
2005 | mysql_thread_end(); |
2006 | |
2007 | pthread_mutex_lock(&counter_mutex); |
2008 | thread_counter--; |
2009 | pthread_cond_signal(&count_threshhold); |
2010 | pthread_mutex_unlock(&counter_mutex); |
2011 | |
2012 | DBUG_RETURN(0); |
2013 | } |
2014 | |
2015 | int |
2016 | parse_option(const char *origin, option_string **stmt, char delm) |
2017 | { |
2018 | char *retstr; |
2019 | char *ptr= (char *)origin; |
2020 | option_string **sptr= stmt; |
2021 | option_string *tmp; |
2022 | size_t length= strlen(origin); |
2023 | uint count= 0; /* We know that there is always one */ |
2024 | |
2025 | for (tmp= *sptr= (option_string *)my_malloc(sizeof(option_string), |
2026 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
2027 | (retstr= strchr(ptr, delm)); |
2028 | tmp->next= (option_string *)my_malloc(sizeof(option_string), |
2029 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)), |
2030 | tmp= tmp->next) |
2031 | { |
2032 | /* |
2033 | Initialize buffer, because otherwise an |
2034 | --engine=<storage_engine>:<option>,<eng1>,<eng2> |
2035 | will crash. |
2036 | */ |
2037 | char buffer[HUGE_STRING_LENGTH]= "" ; |
2038 | char *buffer_ptr; |
2039 | |
2040 | /* |
2041 | Return an error if the length of the any of the comma seprated value |
2042 | exceeds HUGE_STRING_LENGTH. |
2043 | */ |
2044 | if ((size_t)(retstr - ptr) > HUGE_STRING_LENGTH) |
2045 | return -1; |
2046 | |
2047 | count++; |
2048 | strncpy(buffer, ptr, (size_t)(retstr - ptr)); |
2049 | /* |
2050 | Handle --engine=memory:max_row=200 cases, or more general speaking |
2051 | --engine=<storage_engine>:<options>, which will be translated to |
2052 | Engine = storage_engine option. |
2053 | */ |
2054 | if ((buffer_ptr= strchr(buffer, ':'))) |
2055 | { |
2056 | char *option_ptr; |
2057 | |
2058 | tmp->length= (size_t)(buffer_ptr - buffer); |
2059 | tmp->string= my_strndup(ptr, (uint)tmp->length, MYF(MY_FAE)); |
2060 | |
2061 | option_ptr= ptr + 1 + tmp->length; |
2062 | |
2063 | /* Move past the : and the first string */ |
2064 | tmp->option_length= (size_t)(retstr - option_ptr); |
2065 | tmp->option= my_strndup(option_ptr, (uint)tmp->option_length, |
2066 | MYF(MY_FAE)); |
2067 | } |
2068 | else |
2069 | { |
2070 | tmp->string= my_strndup(ptr, (size_t)(retstr - ptr), MYF(MY_FAE)); |
2071 | tmp->length= (size_t)(retstr - ptr); |
2072 | } |
2073 | |
2074 | /* Skip delimiter delm */ |
2075 | ptr+= retstr - ptr + 1; |
2076 | if (isspace(*ptr)) |
2077 | ptr++; |
2078 | |
2079 | count++; |
2080 | } |
2081 | |
2082 | if (ptr != origin + length) |
2083 | { |
2084 | char *origin_ptr; |
2085 | |
2086 | /* |
2087 | Return an error if the length of the any of the comma seprated value |
2088 | exceeds HUGE_STRING_LENGTH. |
2089 | */ |
2090 | if (strlen(ptr) > HUGE_STRING_LENGTH) |
2091 | return -1; |
2092 | |
2093 | if ((origin_ptr= strchr(ptr, ':'))) |
2094 | { |
2095 | char *option_ptr; |
2096 | |
2097 | tmp->length= (size_t)(origin_ptr - ptr); |
2098 | tmp->string= my_strndup(ptr, tmp->length, MYF(MY_FAE)); |
2099 | |
2100 | option_ptr= (char *)ptr + 1 + tmp->length; |
2101 | |
2102 | /* Move past the : and the first string */ |
2103 | tmp->option_length= strlen(option_ptr); |
2104 | tmp->option= my_strndup(option_ptr, tmp->option_length, |
2105 | MYF(MY_FAE)); |
2106 | } |
2107 | else |
2108 | { |
2109 | tmp->length= strlen(ptr); |
2110 | tmp->string= my_strndup(ptr, tmp->length, MYF(MY_FAE)); |
2111 | } |
2112 | |
2113 | count++; |
2114 | } |
2115 | |
2116 | return count; |
2117 | } |
2118 | |
2119 | |
2120 | uint |
2121 | parse_delimiter(const char *script, statement **stmt, char delm) |
2122 | { |
2123 | char *retstr; |
2124 | char *ptr= (char *)script; |
2125 | statement **sptr= stmt; |
2126 | statement *tmp; |
2127 | size_t length= strlen(script); |
2128 | uint count= 0; /* We know that there is always one */ |
2129 | |
2130 | for (tmp= *sptr= (statement *)my_malloc(sizeof(statement), |
2131 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
2132 | (retstr= strchr(ptr, delm)); |
2133 | tmp->next= (statement *)my_malloc(sizeof(statement), |
2134 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)), |
2135 | tmp= tmp->next) |
2136 | { |
2137 | count++; |
2138 | tmp->string= my_strndup(ptr, (uint)(retstr - ptr), MYF(MY_FAE)); |
2139 | tmp->length= (size_t)(retstr - ptr); |
2140 | ptr+= retstr - ptr + 1; |
2141 | if (isspace(*ptr)) |
2142 | ptr++; |
2143 | } |
2144 | |
2145 | if (ptr != script+length) |
2146 | { |
2147 | tmp->string= my_strndup(ptr, (uint)((script + length) - ptr), |
2148 | MYF(MY_FAE)); |
2149 | tmp->length= (size_t)((script + length) - ptr); |
2150 | count++; |
2151 | } |
2152 | |
2153 | return count; |
2154 | } |
2155 | |
2156 | |
2157 | uint |
2158 | parse_comma(const char *string, uint **range) |
2159 | { |
2160 | uint count= 1,x; /* We know that there is always one */ |
2161 | char *retstr; |
2162 | char *ptr= (char *)string; |
2163 | uint *nptr; |
2164 | |
2165 | for (;*ptr; ptr++) |
2166 | if (*ptr == ',') count++; |
2167 | |
2168 | /* One extra spot for the NULL */ |
2169 | nptr= *range= (uint *)my_malloc(sizeof(uint) * (count + 1), |
2170 | MYF(MY_ZEROFILL|MY_FAE|MY_WME)); |
2171 | |
2172 | ptr= (char *)string; |
2173 | x= 0; |
2174 | while ((retstr= strchr(ptr,','))) |
2175 | { |
2176 | nptr[x++]= atoi(ptr); |
2177 | ptr+= retstr - ptr + 1; |
2178 | } |
2179 | nptr[x++]= atoi(ptr); |
2180 | |
2181 | return count; |
2182 | } |
2183 | |
2184 | void |
2185 | print_conclusions(conclusions *con) |
2186 | { |
2187 | printf("Benchmark\n" ); |
2188 | if (con->engine) |
2189 | printf("\tRunning for engine %s\n" , con->engine); |
2190 | printf("\tAverage number of seconds to run all queries: %ld.%03ld seconds\n" , |
2191 | con->avg_timing / 1000, con->avg_timing % 1000); |
2192 | printf("\tMinimum number of seconds to run all queries: %ld.%03ld seconds\n" , |
2193 | con->min_timing / 1000, con->min_timing % 1000); |
2194 | printf("\tMaximum number of seconds to run all queries: %ld.%03ld seconds\n" , |
2195 | con->max_timing / 1000, con->max_timing % 1000); |
2196 | printf("\tNumber of clients running queries: %d\n" , con->users); |
2197 | printf("\tAverage number of queries per client: %llu\n" , con->avg_rows); |
2198 | printf("\n" ); |
2199 | } |
2200 | |
2201 | void |
2202 | print_conclusions_csv(conclusions *con) |
2203 | { |
2204 | char buffer[HUGE_STRING_LENGTH]; |
2205 | const char *ptr= auto_generate_sql_type ? auto_generate_sql_type : "query" ; |
2206 | |
2207 | snprintf(buffer, HUGE_STRING_LENGTH, |
2208 | "%s,%s,%ld.%03ld,%ld.%03ld,%ld.%03ld,%d,%llu\n" , |
2209 | con->engine ? con->engine : "" , /* Storage engine we ran against */ |
2210 | ptr, /* Load type */ |
2211 | con->avg_timing / 1000, con->avg_timing % 1000, /* Time to load */ |
2212 | con->min_timing / 1000, con->min_timing % 1000, /* Min time */ |
2213 | con->max_timing / 1000, con->max_timing % 1000, /* Max time */ |
2214 | con->users, /* Children used */ |
2215 | con->avg_rows /* Queries run */ |
2216 | ); |
2217 | my_write(csv_file, (uchar*) buffer, (uint)strlen(buffer), MYF(0)); |
2218 | } |
2219 | |
2220 | void |
2221 | generate_stats(conclusions *con, option_string *eng, stats *sptr) |
2222 | { |
2223 | stats *ptr; |
2224 | unsigned int x; |
2225 | |
2226 | con->min_timing= sptr->timing; |
2227 | con->max_timing= sptr->timing; |
2228 | con->min_rows= sptr->rows; |
2229 | con->max_rows= sptr->rows; |
2230 | |
2231 | /* At the moment we assume uniform */ |
2232 | con->users= sptr->users; |
2233 | con->avg_rows= sptr->rows; |
2234 | |
2235 | /* With no next, we know it is the last element that was malloced */ |
2236 | for (ptr= sptr, x= 0; x < iterations; ptr++, x++) |
2237 | { |
2238 | con->avg_timing+= ptr->timing; |
2239 | |
2240 | if (ptr->timing > con->max_timing) |
2241 | con->max_timing= ptr->timing; |
2242 | if (ptr->timing < con->min_timing) |
2243 | con->min_timing= ptr->timing; |
2244 | } |
2245 | con->avg_timing= con->avg_timing/iterations; |
2246 | |
2247 | if (eng && eng->string) |
2248 | con->engine= eng->string; |
2249 | else |
2250 | con->engine= NULL; |
2251 | } |
2252 | |
2253 | void |
2254 | option_cleanup(option_string *stmt) |
2255 | { |
2256 | option_string *ptr, *nptr; |
2257 | if (!stmt) |
2258 | return; |
2259 | |
2260 | for (ptr= stmt; ptr; ptr= nptr) |
2261 | { |
2262 | nptr= ptr->next; |
2263 | my_free(ptr->string); |
2264 | my_free(ptr->option); |
2265 | my_free(ptr); |
2266 | } |
2267 | } |
2268 | |
2269 | void |
2270 | statement_cleanup(statement *stmt) |
2271 | { |
2272 | statement *ptr, *nptr; |
2273 | if (!stmt) |
2274 | return; |
2275 | |
2276 | for (ptr= stmt; ptr; ptr= nptr) |
2277 | { |
2278 | nptr= ptr->next; |
2279 | my_free(ptr->string); |
2280 | my_free(ptr); |
2281 | } |
2282 | } |
2283 | |
2284 | |
2285 | int |
2286 | slap_connect(MYSQL *mysql) |
2287 | { |
2288 | /* Connect to server */ |
2289 | static ulong connection_retry_sleep= 100000; /* Microseconds */ |
2290 | int x, connect_error= 1; |
2291 | for (x= 0; x < 10; x++) |
2292 | { |
2293 | set_mysql_connect_options(mysql); |
2294 | if (opt_init_command) |
2295 | mysql_options(mysql, MYSQL_INIT_COMMAND, opt_init_command); |
2296 | if (mysql_real_connect(mysql, host, user, opt_password, |
2297 | create_schema_string, |
2298 | opt_mysql_port, |
2299 | opt_mysql_unix_port, |
2300 | connect_flags)) |
2301 | { |
2302 | /* Connect suceeded */ |
2303 | connect_error= 0; |
2304 | break; |
2305 | } |
2306 | my_sleep(connection_retry_sleep); |
2307 | } |
2308 | if (connect_error) |
2309 | { |
2310 | fprintf(stderr,"%s: Error when connecting to server: %d %s\n" , |
2311 | my_progname, mysql_errno(mysql), mysql_error(mysql)); |
2312 | return 1; |
2313 | } |
2314 | |
2315 | return 0; |
2316 | } |
2317 | |