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-1335  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 
timedif(struct timeval a,struct timeval b)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__
gettimeofday(struct timeval * tp,void * tzp)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 
set_mysql_connect_options(MYSQL * mysql)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 
main(int argc,char ** argv)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 
concurrency_loop(MYSQL * mysql,uint current,option_string * eptr)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 
print_version(void)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 
usage(void)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
get_one_option(int optid,const struct my_option * opt,char * argument)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
get_random_string(char * buf)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 *
build_table_string(void)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(36) 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(36) 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 *
build_update_string(void)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 *
build_insert_string(void)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 *
build_select_string(my_bool key)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
get_options(int * argc,char *** argv)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   /*
1197     If something is created and --no-drop is not specified, we drop the
1198     schema.
1199   */
1200   if (!opt_no_drop && (create_string || auto_generate_sql))
1201     opt_preserve= FALSE;
1202 
1203   if (auto_generate_sql && (create_string || user_supplied_query))
1204   {
1205       fprintf(stderr,
1206               "%s: Can't use --auto-generate-sql when create and query strings are specified!\n",
1207               my_progname);
1208       exit(1);
1209   }
1210 
1211   if (auto_generate_sql && auto_generate_sql_guid_primary &&
1212       auto_generate_sql_autoincrement)
1213   {
1214       fprintf(stderr,
1215               "%s: Either auto-generate-sql-guid-primary or auto-generate-sql-add-autoincrement can be used!\n",
1216               my_progname);
1217       exit(1);
1218   }
1219 
1220   /*
1221     We are testing to make sure that if someone specified a key search
1222     that we actually added a key!
1223   */
1224   if (auto_generate_sql && auto_generate_sql_type[0] == 'k')
1225     if ( auto_generate_sql_autoincrement == FALSE &&
1226          auto_generate_sql_guid_primary == FALSE)
1227     {
1228       fprintf(stderr,
1229               "%s: Can't perform key test without a primary key!\n",
1230               my_progname);
1231       exit(1);
1232     }
1233 
1234   if (auto_generate_sql && num_of_query && auto_actual_queries)
1235   {
1236       fprintf(stderr,
1237               "%s: Either auto-generate-sql-execute-number or number-of-queries can be used!\n",
1238               my_progname);
1239       exit(1);
1240   }
1241 
1242   parse_comma(concurrency_str ? concurrency_str : "1", &concurrency);
1243 
1244   if (opt_csv_str)
1245   {
1246     opt_silent= TRUE;
1247 
1248     if (opt_csv_str[0] == '-')
1249     {
1250       csv_file= my_fileno(stdout);
1251     }
1252     else
1253     {
1254       if ((csv_file= my_open(opt_csv_str, O_CREAT|O_WRONLY|O_APPEND, MYF(0)))
1255           == -1)
1256       {
1257         fprintf(stderr,"%s: Could not open csv file: %sn\n",
1258                 my_progname, opt_csv_str);
1259         exit(1);
1260       }
1261     }
1262   }
1263 
1264   if (opt_only_print)
1265     opt_silent= TRUE;
1266 
1267   if (num_int_cols_opt)
1268   {
1269     option_string *str;
1270     if(parse_option(num_int_cols_opt, &str, ',') == -1)
1271     {
1272       fprintf(stderr, "Invalid value specified for the option "
1273               "'number-int-cols'\n");
1274       option_cleanup(str);
1275       return 1;
1276     }
1277     num_int_cols= atoi(str->string);
1278     if (str->option)
1279       num_int_cols_index= atoi(str->option);
1280 
1281     option_cleanup(str);
1282   }
1283 
1284   if (num_char_cols_opt)
1285   {
1286     option_string *str;
1287     if(parse_option(num_char_cols_opt, &str, ',') == -1)
1288     {
1289       fprintf(stderr, "Invalid value specified for the option "
1290               "'number-char-cols'\n");
1291       option_cleanup(str);
1292       return 1;
1293     }
1294     num_char_cols= atoi(str->string);
1295     if (str->option)
1296       num_char_cols_index= atoi(str->option);
1297     else
1298       num_char_cols_index= 0;
1299 
1300     option_cleanup(str);
1301   }
1302 
1303 
1304   if (auto_generate_sql)
1305   {
1306     unsigned long long x= 0;
1307     statement *ptr_statement;
1308 
1309     if (verbose >= 2)
1310       printf("Building Create Statements for Auto\n");
1311 
1312     create_statements= build_table_string();
1313     /*
1314       Pre-populate table
1315     */
1316     for (ptr_statement= create_statements, x= 0;
1317          x < auto_generate_sql_unique_write_number;
1318          x++, ptr_statement= ptr_statement->next)
1319     {
1320       ptr_statement->next= build_insert_string();
1321     }
1322 
1323     if (verbose >= 2)
1324       printf("Building Query Statements for Auto\n");
1325 
1326     if (auto_generate_sql_type[0] == 'r')
1327     {
1328       if (verbose >= 2)
1329         printf("Generating SELECT Statements for Auto\n");
1330 
1331       query_statements= build_select_string(FALSE);
1332       for (ptr_statement= query_statements, x= 0;
1333            x < auto_generate_sql_unique_query_number;
1334            x++, ptr_statement= ptr_statement->next)
1335       {
1336         ptr_statement->next= build_select_string(FALSE);
1337       }
1338     }
1339     else if (auto_generate_sql_type[0] == 'k')
1340     {
1341       if (verbose >= 2)
1342         printf("Generating SELECT for keys Statements for Auto\n");
1343 
1344       query_statements= build_select_string(TRUE);
1345       for (ptr_statement= query_statements, x= 0;
1346            x < auto_generate_sql_unique_query_number;
1347            x++, ptr_statement= ptr_statement->next)
1348       {
1349         ptr_statement->next= build_select_string(TRUE);
1350       }
1351     }
1352     else if (auto_generate_sql_type[0] == 'w')
1353     {
1354       /*
1355         We generate a number of strings in case the engine is
1356         Archive (since strings which were identical one after another
1357         would be too easily optimized).
1358       */
1359       if (verbose >= 2)
1360         printf("Generating INSERT Statements for Auto\n");
1361       query_statements= build_insert_string();
1362       for (ptr_statement= query_statements, x= 0;
1363            x < auto_generate_sql_unique_query_number;
1364            x++, ptr_statement= ptr_statement->next)
1365       {
1366         ptr_statement->next= build_insert_string();
1367       }
1368     }
1369     else if (auto_generate_sql_type[0] == 'u')
1370     {
1371       query_statements= build_update_string();
1372       for (ptr_statement= query_statements, x= 0;
1373            x < auto_generate_sql_unique_query_number;
1374            x++, ptr_statement= ptr_statement->next)
1375       {
1376           ptr_statement->next= build_update_string();
1377       }
1378     }
1379     else /* Mixed mode is default */
1380     {
1381       int coin= 0;
1382 
1383       query_statements= build_insert_string();
1384       /*
1385         This logic should be extended to do a more mixed load,
1386         at the moment it results in "every other".
1387       */
1388       for (ptr_statement= query_statements, x= 0;
1389            x < auto_generate_sql_unique_query_number;
1390            x++, ptr_statement= ptr_statement->next)
1391       {
1392         if (coin)
1393         {
1394           ptr_statement->next= build_insert_string();
1395           coin= 0;
1396         }
1397         else
1398         {
1399           ptr_statement->next= build_select_string(TRUE);
1400           coin= 1;
1401         }
1402       }
1403     }
1404   }
1405   else
1406   {
1407     if (create_string && my_stat(create_string, &sbuf, MYF(0)))
1408     {
1409       File data_file;
1410       if (!MY_S_ISREG(sbuf.st_mode))
1411       {
1412         fprintf(stderr,"%s: Create file was not a regular file\n",
1413                 my_progname);
1414         exit(1);
1415       }
1416       if ((data_file= my_open(create_string, O_RDWR, MYF(0))) == -1)
1417       {
1418         fprintf(stderr,"%s: Could not open create file\n", my_progname);
1419         exit(1);
1420       }
1421       tmp_string= (char *)my_malloc((size_t)sbuf.st_size + 1,
1422                               MYF(MY_ZEROFILL|MY_FAE|MY_WME));
1423       my_read(data_file, (uchar*) tmp_string, (size_t)sbuf.st_size, MYF(0));
1424       tmp_string[sbuf.st_size]= '\0';
1425       my_close(data_file,MYF(0));
1426       parse_delimiter(tmp_string, &create_statements, delimiter[0]);
1427       my_free(tmp_string);
1428     }
1429     else if (create_string)
1430     {
1431         parse_delimiter(create_string, &create_statements, delimiter[0]);
1432     }
1433 
1434     if (user_supplied_query && my_stat(user_supplied_query, &sbuf, MYF(0)))
1435     {
1436       File data_file;
1437       if (!MY_S_ISREG(sbuf.st_mode))
1438       {
1439         fprintf(stderr,"%s: User query supplied file was not a regular file\n",
1440                 my_progname);
1441         exit(1);
1442       }
1443       if ((data_file= my_open(user_supplied_query, O_RDWR, MYF(0))) == -1)
1444       {
1445         fprintf(stderr,"%s: Could not open query supplied file\n", my_progname);
1446         exit(1);
1447       }
1448       tmp_string= (char *)my_malloc((size_t)sbuf.st_size + 1,
1449                                     MYF(MY_ZEROFILL|MY_FAE|MY_WME));
1450       my_read(data_file, (uchar*) tmp_string, (size_t)sbuf.st_size, MYF(0));
1451       tmp_string[sbuf.st_size]= '\0';
1452       my_close(data_file,MYF(0));
1453       if (user_supplied_query)
1454         actual_queries= parse_delimiter(tmp_string, &query_statements,
1455                                         delimiter[0]);
1456       my_free(tmp_string);
1457     }
1458     else if (user_supplied_query)
1459     {
1460         actual_queries= parse_delimiter(user_supplied_query, &query_statements,
1461                                         delimiter[0]);
1462     }
1463   }
1464 
1465   if (user_supplied_pre_statements && my_stat(user_supplied_pre_statements, &sbuf, MYF(0)))
1466   {
1467     File data_file;
1468     if (!MY_S_ISREG(sbuf.st_mode))
1469     {
1470       fprintf(stderr,"%s: User query supplied file was not a regular file\n",
1471               my_progname);
1472       exit(1);
1473     }
1474     if ((data_file= my_open(user_supplied_pre_statements, O_RDWR, MYF(0))) == -1)
1475     {
1476       fprintf(stderr,"%s: Could not open query supplied file\n", my_progname);
1477       exit(1);
1478     }
1479     tmp_string= (char *)my_malloc((size_t)sbuf.st_size + 1,
1480                                   MYF(MY_ZEROFILL|MY_FAE|MY_WME));
1481     my_read(data_file, (uchar*) tmp_string, (size_t)sbuf.st_size, MYF(0));
1482     tmp_string[sbuf.st_size]= '\0';
1483     my_close(data_file,MYF(0));
1484     if (user_supplied_pre_statements)
1485       (void)parse_delimiter(tmp_string, &pre_statements,
1486                             delimiter[0]);
1487     my_free(tmp_string);
1488   }
1489   else if (user_supplied_pre_statements)
1490   {
1491     (void)parse_delimiter(user_supplied_pre_statements,
1492                           &pre_statements,
1493                           delimiter[0]);
1494   }
1495 
1496   if (user_supplied_post_statements && my_stat(user_supplied_post_statements, &sbuf, MYF(0)))
1497   {
1498     File data_file;
1499     if (!MY_S_ISREG(sbuf.st_mode))
1500     {
1501       fprintf(stderr,"%s: User query supplied file was not a regular file\n",
1502               my_progname);
1503       exit(1);
1504     }
1505     if ((data_file= my_open(user_supplied_post_statements, O_RDWR, MYF(0))) == -1)
1506     {
1507       fprintf(stderr,"%s: Could not open query supplied file\n", my_progname);
1508       exit(1);
1509     }
1510     tmp_string= (char *)my_malloc((size_t)sbuf.st_size + 1,
1511                                   MYF(MY_ZEROFILL|MY_FAE|MY_WME));
1512     my_read(data_file, (uchar*) tmp_string, (size_t)sbuf.st_size, MYF(0));
1513     tmp_string[sbuf.st_size]= '\0';
1514     my_close(data_file,MYF(0));
1515     if (user_supplied_post_statements)
1516       (void)parse_delimiter(tmp_string, &post_statements,
1517                             delimiter[0]);
1518     my_free(tmp_string);
1519   }
1520   else if (user_supplied_post_statements)
1521   {
1522     (void)parse_delimiter(user_supplied_post_statements, &post_statements,
1523                           delimiter[0]);
1524   }
1525 
1526   if (verbose >= 2)
1527     printf("Parsing engines to use.\n");
1528 
1529   if (default_engine)
1530   {
1531     if(parse_option(default_engine, &engine_options, ',') == -1)
1532     {
1533       fprintf(stderr, "Invalid value specified for the option 'engine'\n");
1534       return 1;
1535     }
1536   }
1537 
1538   if (tty_password)
1539     opt_password= get_tty_password(NullS);
1540 
1541   DBUG_RETURN(0);
1542 }
1543 
1544 
run_query(MYSQL * mysql,const char * query,size_t len)1545 static int run_query(MYSQL *mysql, const char *query, size_t len)
1546 {
1547   if (opt_only_print)
1548   {
1549     printf("%.*s;\n", (int)len, query);
1550     return 0;
1551   }
1552 
1553   if (verbose >= 3)
1554     printf("%.*s;\n", (int)len, query);
1555 
1556   return mysql_real_query(mysql, query, (ulong)len);
1557 }
1558 
1559 
1560 static int
generate_primary_key_list(MYSQL * mysql,option_string * engine_stmt)1561 generate_primary_key_list(MYSQL *mysql, option_string *engine_stmt)
1562 {
1563   MYSQL_RES *result;
1564   MYSQL_ROW row;
1565   unsigned long long counter;
1566   DBUG_ENTER("generate_primary_key_list");
1567 
1568   /*
1569     Blackhole is a special case, this allows us to test the upper end
1570     of the server during load runs.
1571   */
1572   if (opt_only_print || (engine_stmt &&
1573                          strstr(engine_stmt->string, "blackhole")))
1574   {
1575     primary_keys_number_of= 1;
1576     primary_keys= (char **)my_malloc((uint)(sizeof(char *) *
1577                                             primary_keys_number_of),
1578                                     MYF(MY_ZEROFILL|MY_FAE|MY_WME));
1579     /* Yes, we strdup a const string to simplify the interface */
1580     primary_keys[0]= my_strdup("796c4422-1d94-102a-9d6d-00e0812d", MYF(0));
1581   }
1582   else
1583   {
1584     if (run_query(mysql, "SELECT id from t1", strlen("SELECT id from t1")))
1585     {
1586       fprintf(stderr,"%s: Cannot select GUID primary keys. (%s)\n", my_progname,
1587               mysql_error(mysql));
1588       exit(1);
1589     }
1590 
1591     if (!(result= mysql_store_result(mysql)))
1592     {
1593       fprintf(stderr, "%s: Error when storing result: %d %s\n",
1594               my_progname, mysql_errno(mysql), mysql_error(mysql));
1595       exit(1);
1596     }
1597     primary_keys_number_of= mysql_num_rows(result);
1598 
1599     /* So why check this? Blackhole :) */
1600     if (primary_keys_number_of)
1601     {
1602       /*
1603         We create the structure and loop and create the items.
1604       */
1605       primary_keys= (char **)my_malloc((uint)(sizeof(char *) *
1606                                               primary_keys_number_of),
1607                                        MYF(MY_ZEROFILL|MY_FAE|MY_WME));
1608       row= mysql_fetch_row(result);
1609       for (counter= 0; counter < primary_keys_number_of;
1610            counter++, row= mysql_fetch_row(result))
1611         primary_keys[counter]= my_strdup(row[0], MYF(0));
1612     }
1613 
1614     mysql_free_result(result);
1615   }
1616 
1617   DBUG_RETURN(0);
1618 }
1619 
1620 static int
drop_primary_key_list(void)1621 drop_primary_key_list(void)
1622 {
1623   unsigned long long counter;
1624 
1625   if (primary_keys_number_of)
1626   {
1627     for (counter= 0; counter < primary_keys_number_of; counter++)
1628       my_free(primary_keys[counter]);
1629 
1630     my_free(primary_keys);
1631   }
1632 
1633   return 0;
1634 }
1635 
1636 static int
create_schema(MYSQL * mysql,const char * db,statement * stmt,option_string * engine_stmt)1637 create_schema(MYSQL *mysql, const char *db, statement *stmt,
1638               option_string *engine_stmt)
1639 {
1640   char query[HUGE_STRING_LENGTH];
1641   statement *ptr;
1642   statement *after_create;
1643   int len;
1644   ulonglong count;
1645   DBUG_ENTER("create_schema");
1646 
1647   len= snprintf(query, HUGE_STRING_LENGTH, "CREATE SCHEMA `%s`", db);
1648 
1649   if (verbose >= 2)
1650     printf("Loading Pre-data\n");
1651 
1652   if (run_query(mysql, query, len))
1653   {
1654     fprintf(stderr,"%s: Cannot create schema %s : %s\n", my_progname, db,
1655             mysql_error(mysql));
1656     exit(1);
1657   }
1658 
1659   if (opt_only_print)
1660   {
1661     printf("use %s;\n", db);
1662   }
1663   else
1664   {
1665     if (verbose >= 3)
1666       printf("%s;\n", query);
1667 
1668     if (mysql_select_db(mysql,  db))
1669     {
1670       fprintf(stderr,"%s: Cannot select schema '%s': %s\n",my_progname, db,
1671               mysql_error(mysql));
1672       exit(1);
1673     }
1674   }
1675 
1676   count= 0;
1677   after_create= stmt;
1678 
1679 limit_not_met:
1680   for (ptr= after_create; ptr && ptr->length; ptr= ptr->next, count++)
1681   {
1682     if (auto_generate_sql && ( auto_generate_sql_number == count))
1683       break;
1684 
1685     if (engine_stmt && engine_stmt->option && ptr->type == CREATE_TABLE_TYPE)
1686     {
1687       char buffer[HUGE_STRING_LENGTH];
1688 
1689       snprintf(buffer, HUGE_STRING_LENGTH, "%s Engine = %s %s",
1690                ptr->string, engine_stmt->string, engine_stmt->option);
1691       if (run_query(mysql, buffer, strlen(buffer)))
1692       {
1693         fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n",
1694                 my_progname, (uint)ptr->length, ptr->string, mysql_error(mysql));
1695         exit(1);
1696       }
1697     }
1698     else if (engine_stmt && engine_stmt->string && ptr->type == CREATE_TABLE_TYPE)
1699     {
1700       char buffer[HUGE_STRING_LENGTH];
1701 
1702       snprintf(buffer, HUGE_STRING_LENGTH, "%s Engine = %s",
1703                ptr->string, engine_stmt->string);
1704       if (run_query(mysql, buffer, strlen(buffer)))
1705       {
1706         fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n",
1707                 my_progname, (uint)ptr->length, ptr->string, mysql_error(mysql));
1708         exit(1);
1709       }
1710     }
1711     else
1712     {
1713       if (run_query(mysql, ptr->string, ptr->length))
1714       {
1715         fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n",
1716                 my_progname, (uint)ptr->length, ptr->string, mysql_error(mysql));
1717         exit(1);
1718       }
1719     }
1720   }
1721 
1722   if (auto_generate_sql && (auto_generate_sql_number > count ))
1723   {
1724     /* Special case for auto create, we don't want to create tables twice */
1725     after_create= stmt->next;
1726     goto limit_not_met;
1727   }
1728 
1729   DBUG_RETURN(0);
1730 }
1731 
1732 static int
drop_schema(MYSQL * mysql,const char * db)1733 drop_schema(MYSQL *mysql, const char *db)
1734 {
1735   char query[HUGE_STRING_LENGTH];
1736   int len;
1737 
1738   DBUG_ENTER("drop_schema");
1739   len= snprintf(query, HUGE_STRING_LENGTH, "DROP SCHEMA IF EXISTS `%s`", db);
1740 
1741   if (run_query(mysql, query, len))
1742   {
1743     fprintf(stderr,"%s: Cannot drop database '%s' ERROR : %s\n",
1744             my_progname, db, mysql_error(mysql));
1745     exit(1);
1746   }
1747 
1748 
1749 
1750   DBUG_RETURN(0);
1751 }
1752 
1753 static int
run_statements(MYSQL * mysql,statement * stmt)1754 run_statements(MYSQL *mysql, statement *stmt)
1755 {
1756   statement *ptr;
1757   MYSQL_RES *result;
1758   DBUG_ENTER("run_statements");
1759 
1760   for (ptr= stmt; ptr && ptr->length; ptr= ptr->next)
1761   {
1762     if (run_query(mysql, ptr->string, ptr->length))
1763     {
1764       fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n",
1765               my_progname, (uint)ptr->length, ptr->string, mysql_error(mysql));
1766       exit(1);
1767     }
1768     if (mysql_field_count(mysql))
1769     {
1770       result= mysql_store_result(mysql);
1771       mysql_free_result(result);
1772     }
1773   }
1774 
1775   DBUG_RETURN(0);
1776 }
1777 
1778 static int
run_scheduler(stats * sptr,statement * stmts,uint concur,ulonglong limit)1779 run_scheduler(stats *sptr, statement *stmts, uint concur, ulonglong limit)
1780 {
1781   uint x;
1782   struct timeval start_time, end_time;
1783   thread_context con;
1784   pthread_t mainthread;            /* Thread descriptor */
1785   pthread_attr_t attr;          /* Thread attributes */
1786   DBUG_ENTER("run_scheduler");
1787 
1788   con.stmt= stmts;
1789   con.limit= limit;
1790 
1791   pthread_attr_init(&attr);
1792   pthread_attr_setdetachstate(&attr,
1793 		  PTHREAD_CREATE_DETACHED);
1794 
1795   pthread_mutex_lock(&counter_mutex);
1796   thread_counter= 0;
1797 
1798   pthread_mutex_lock(&sleeper_mutex);
1799   master_wakeup= 1;
1800   pthread_mutex_unlock(&sleeper_mutex);
1801   for (x= 0; x < concur; x++)
1802   {
1803     /* now you create the thread */
1804     if (pthread_create(&mainthread, &attr, run_task,
1805                        (void *)&con) != 0)
1806     {
1807       fprintf(stderr,"%s: Could not create thread\n",
1808               my_progname);
1809       exit(0);
1810     }
1811     thread_counter++;
1812   }
1813   pthread_mutex_unlock(&counter_mutex);
1814   pthread_attr_destroy(&attr);
1815 
1816   pthread_mutex_lock(&sleeper_mutex);
1817   master_wakeup= 0;
1818   pthread_cond_broadcast(&sleep_threshhold);
1819   pthread_mutex_unlock(&sleeper_mutex);
1820 
1821   gettimeofday(&start_time, NULL);
1822 
1823   /*
1824     We loop until we know that all children have cleaned up.
1825   */
1826   pthread_mutex_lock(&counter_mutex);
1827   while (thread_counter)
1828   {
1829     struct timespec abstime;
1830 
1831     set_timespec(abstime, 3);
1832     pthread_cond_timedwait(&count_threshhold, &counter_mutex, &abstime);
1833   }
1834   pthread_mutex_unlock(&counter_mutex);
1835 
1836   gettimeofday(&end_time, NULL);
1837 
1838 
1839   sptr->timing= timedif(end_time, start_time);
1840   sptr->users= concur;
1841   sptr->rows= limit;
1842 
1843   DBUG_RETURN(0);
1844 }
1845 
1846 
run_task(void * p)1847 pthread_handler_t run_task(void *p)
1848 {
1849   ulonglong counter= 0, queries;
1850   ulonglong detach_counter;
1851   unsigned int commit_counter;
1852   MYSQL *mysql;
1853   MYSQL_RES *result;
1854   MYSQL_ROW row;
1855   statement *ptr;
1856   thread_context *con= (thread_context *)p;
1857 
1858   DBUG_ENTER("run_task");
1859   DBUG_PRINT("info", ("task script \"%s\"", con->stmt ? con->stmt->string : ""));
1860 
1861   pthread_mutex_lock(&sleeper_mutex);
1862   while (master_wakeup)
1863   {
1864     pthread_cond_wait(&sleep_threshhold, &sleeper_mutex);
1865   }
1866   pthread_mutex_unlock(&sleeper_mutex);
1867 
1868   if (mysql_thread_init())
1869   {
1870     fprintf(stderr,"%s: mysql_thread_init() failed\n", my_progname);
1871     exit(0);
1872   }
1873 
1874   if (!(mysql= mysql_init(NULL)))
1875   {
1876     fprintf(stderr,"%s: mysql_init() failed\n", my_progname);
1877     mysql_thread_end();
1878     exit(0);
1879   }
1880 
1881   set_mysql_connect_options(mysql);
1882 
1883   DBUG_PRINT("info", ("trying to connect to host %s as user %s", host, user));
1884 
1885   if (!opt_only_print)
1886   {
1887     if (slap_connect(mysql))
1888       goto end;
1889   }
1890 
1891   DBUG_PRINT("info", ("connected."));
1892   if (verbose >= 3)
1893     printf("connected!\n");
1894   queries= 0;
1895 
1896   commit_counter= 0;
1897   if (commit_rate)
1898     run_query(mysql, "SET AUTOCOMMIT=0", strlen("SET AUTOCOMMIT=0"));
1899 
1900 limit_not_met:
1901     for (ptr= con->stmt, detach_counter= 0;
1902          ptr && ptr->length;
1903          ptr= ptr->next, detach_counter++)
1904     {
1905       if (!opt_only_print && detach_rate && !(detach_counter % detach_rate))
1906       {
1907         mysql_close(mysql);
1908 
1909         if (!(mysql= mysql_init(NULL)))
1910         {
1911           fprintf(stderr,"%s: mysql_init() failed ERROR : %s\n",
1912                   my_progname, mysql_error(mysql));
1913           exit(0);
1914         }
1915         if (slap_connect(mysql))
1916           goto end;
1917       }
1918 
1919       /*
1920         We have to execute differently based on query type. This should become a function.
1921       */
1922       if ((ptr->type == UPDATE_TYPE_REQUIRES_PREFIX) ||
1923           (ptr->type == SELECT_TYPE_REQUIRES_PREFIX))
1924       {
1925         int length;
1926         unsigned int key_val;
1927         char *key;
1928         char buffer[HUGE_STRING_LENGTH];
1929 
1930         /*
1931           This should only happen if some sort of new engine was
1932           implemented that didn't properly handle UPDATEs.
1933 
1934           Just in case someone runs this under an experimental engine we don't
1935           want a crash so the if() is placed here.
1936         */
1937         DBUG_ASSERT(primary_keys_number_of);
1938         if (primary_keys_number_of)
1939         {
1940           key_val= (unsigned int)(random() % primary_keys_number_of);
1941           key= primary_keys[key_val];
1942 
1943           DBUG_ASSERT(key);
1944 
1945           length= snprintf(buffer, HUGE_STRING_LENGTH, "%.*s '%s'",
1946                            (int)ptr->length, ptr->string, key);
1947 
1948           if (run_query(mysql, buffer, length))
1949           {
1950             fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n",
1951                     my_progname, (uint)length, buffer, mysql_error(mysql));
1952             exit(0);
1953           }
1954         }
1955       }
1956       else
1957       {
1958         if (run_query(mysql, ptr->string, ptr->length))
1959         {
1960           fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n",
1961                   my_progname, (uint)ptr->length, ptr->string, mysql_error(mysql));
1962           exit(0);
1963         }
1964       }
1965 
1966       do
1967       {
1968         if (mysql_field_count(mysql))
1969         {
1970           if (!(result= mysql_store_result(mysql)))
1971             fprintf(stderr, "%s: Error when storing result: %d %s\n",
1972                     my_progname, mysql_errno(mysql), mysql_error(mysql));
1973           else
1974           {
1975             while ((row= mysql_fetch_row(result)))
1976               counter++;
1977             mysql_free_result(result);
1978           }
1979         }
1980       } while(mysql_next_result(mysql) == 0);
1981       queries++;
1982 
1983       if (commit_rate && (++commit_counter == commit_rate))
1984       {
1985         commit_counter= 0;
1986         run_query(mysql, "COMMIT", strlen("COMMIT"));
1987       }
1988 
1989       if (con->limit && queries == con->limit)
1990         goto end;
1991     }
1992 
1993     if (con->limit && queries < con->limit)
1994       goto limit_not_met;
1995 
1996 end:
1997   if (commit_rate)
1998     run_query(mysql, "COMMIT", strlen("COMMIT"));
1999 
2000   mysql_close(mysql);
2001 
2002   mysql_thread_end();
2003 
2004   pthread_mutex_lock(&counter_mutex);
2005   thread_counter--;
2006   pthread_cond_signal(&count_threshhold);
2007   pthread_mutex_unlock(&counter_mutex);
2008 
2009   DBUG_RETURN(0);
2010 }
2011 
2012 int
parse_option(const char * origin,option_string ** stmt,char delm)2013 parse_option(const char *origin, option_string **stmt, char delm)
2014 {
2015   char *retstr;
2016   char *ptr= (char *)origin;
2017   option_string **sptr= stmt;
2018   option_string *tmp;
2019   size_t length= strlen(origin);
2020   uint count= 0; /* We know that there is always one */
2021 
2022   for (tmp= *sptr= (option_string *)my_malloc(sizeof(option_string),
2023                                               MYF(MY_ZEROFILL|MY_FAE|MY_WME));
2024        (retstr= strchr(ptr, delm));
2025        tmp->next=  (option_string *)my_malloc(sizeof(option_string),
2026                                               MYF(MY_ZEROFILL|MY_FAE|MY_WME)),
2027        tmp= tmp->next)
2028   {
2029     /*
2030        Initialize buffer, because otherwise an
2031        --engine=<storage_engine>:<option>,<eng1>,<eng2>
2032        will crash.
2033      */
2034     char buffer[HUGE_STRING_LENGTH]= "";
2035     char *buffer_ptr;
2036 
2037     /*
2038       Return an error if the length of the any of the comma seprated value
2039       exceeds HUGE_STRING_LENGTH.
2040     */
2041     if ((size_t)(retstr - ptr) > HUGE_STRING_LENGTH)
2042       return -1;
2043 
2044     count++;
2045     strncpy(buffer, ptr, (size_t)(retstr - ptr));
2046     /*
2047        Handle --engine=memory:max_row=200 cases, or more general speaking
2048        --engine=<storage_engine>:<options>, which will be translated to
2049        Engine = storage_engine option.
2050      */
2051     if ((buffer_ptr= strchr(buffer, ':')))
2052     {
2053       char *option_ptr;
2054 
2055       tmp->length= (size_t)(buffer_ptr - buffer);
2056       tmp->string= my_strndup(ptr, (uint)tmp->length, MYF(MY_FAE));
2057 
2058       option_ptr= ptr + 1 + tmp->length;
2059 
2060       /* Move past the : and the first string */
2061       tmp->option_length= (size_t)(retstr - option_ptr);
2062       tmp->option= my_strndup(option_ptr, (uint)tmp->option_length,
2063                               MYF(MY_FAE));
2064     }
2065     else
2066     {
2067       tmp->string= my_strndup(ptr, (size_t)(retstr - ptr), MYF(MY_FAE));
2068       tmp->length= (size_t)(retstr - ptr);
2069     }
2070 
2071     /* Skip delimiter delm */
2072     ptr+= retstr - ptr + 1;
2073     if (isspace(*ptr))
2074       ptr++;
2075 
2076     count++;
2077   }
2078 
2079   if (ptr != origin + length)
2080   {
2081     char *origin_ptr;
2082 
2083     /*
2084       Return an error if the length of the any of the comma seprated value
2085       exceeds HUGE_STRING_LENGTH.
2086     */
2087     if (strlen(ptr) > HUGE_STRING_LENGTH)
2088       return -1;
2089 
2090     if ((origin_ptr= strchr(ptr, ':')))
2091     {
2092       char *option_ptr;
2093 
2094       tmp->length= (size_t)(origin_ptr - ptr);
2095       tmp->string= my_strndup(ptr, tmp->length, MYF(MY_FAE));
2096 
2097       option_ptr= (char *)ptr + 1 + tmp->length;
2098 
2099       /* Move past the : and the first string */
2100       tmp->option_length= strlen(option_ptr);
2101       tmp->option= my_strndup(option_ptr, tmp->option_length,
2102                               MYF(MY_FAE));
2103     }
2104     else
2105     {
2106       tmp->length= strlen(ptr);
2107       tmp->string= my_strndup(ptr, tmp->length, MYF(MY_FAE));
2108     }
2109 
2110     count++;
2111   }
2112 
2113   return count;
2114 }
2115 
2116 
2117 uint
parse_delimiter(const char * script,statement ** stmt,char delm)2118 parse_delimiter(const char *script, statement **stmt, char delm)
2119 {
2120   char *retstr;
2121   char *ptr= (char *)script;
2122   statement **sptr= stmt;
2123   statement *tmp;
2124   size_t length= strlen(script);
2125   uint count= 0; /* We know that there is always one */
2126 
2127   for (tmp= *sptr= (statement *)my_malloc(sizeof(statement),
2128                                           MYF(MY_ZEROFILL|MY_FAE|MY_WME));
2129        (retstr= strchr(ptr, delm));
2130        tmp->next=  (statement *)my_malloc(sizeof(statement),
2131                                           MYF(MY_ZEROFILL|MY_FAE|MY_WME)),
2132        tmp= tmp->next)
2133   {
2134     count++;
2135     tmp->string= my_strndup(ptr, (uint)(retstr - ptr), MYF(MY_FAE));
2136     tmp->length= (size_t)(retstr - ptr);
2137     ptr+= retstr - ptr + 1;
2138     if (isspace(*ptr))
2139       ptr++;
2140   }
2141 
2142   if (ptr != script+length)
2143   {
2144     tmp->string= my_strndup(ptr, (uint)((script + length) - ptr),
2145                             MYF(MY_FAE));
2146     tmp->length= (size_t)((script + length) - ptr);
2147     count++;
2148   }
2149 
2150   return count;
2151 }
2152 
2153 
2154 uint
parse_comma(const char * string,uint ** range)2155 parse_comma(const char *string, uint **range)
2156 {
2157   uint count= 1,x; /* We know that there is always one */
2158   char *retstr;
2159   char *ptr= (char *)string;
2160   uint *nptr;
2161 
2162   for (;*ptr; ptr++)
2163     if (*ptr == ',') count++;
2164 
2165   /* One extra spot for the NULL */
2166   nptr= *range= (uint *)my_malloc(sizeof(uint) * (count + 1),
2167                                   MYF(MY_ZEROFILL|MY_FAE|MY_WME));
2168 
2169   ptr= (char *)string;
2170   x= 0;
2171   while ((retstr= strchr(ptr,',')))
2172   {
2173     nptr[x++]= atoi(ptr);
2174     ptr+= retstr - ptr + 1;
2175   }
2176   nptr[x++]= atoi(ptr);
2177 
2178   return count;
2179 }
2180 
2181 void
print_conclusions(conclusions * con)2182 print_conclusions(conclusions *con)
2183 {
2184   printf("Benchmark\n");
2185   if (con->engine)
2186     printf("\tRunning for engine %s\n", con->engine);
2187   printf("\tAverage number of seconds to run all queries: %ld.%03ld seconds\n",
2188                     con->avg_timing / 1000, con->avg_timing % 1000);
2189   printf("\tMinimum number of seconds to run all queries: %ld.%03ld seconds\n",
2190                     con->min_timing / 1000, con->min_timing % 1000);
2191   printf("\tMaximum number of seconds to run all queries: %ld.%03ld seconds\n",
2192                     con->max_timing / 1000, con->max_timing % 1000);
2193   printf("\tNumber of clients running queries: %d\n", con->users);
2194   printf("\tAverage number of queries per client: %llu\n", con->avg_rows);
2195   printf("\n");
2196 }
2197 
2198 void
print_conclusions_csv(conclusions * con)2199 print_conclusions_csv(conclusions *con)
2200 {
2201   char buffer[HUGE_STRING_LENGTH];
2202   const char *ptr= auto_generate_sql_type ? auto_generate_sql_type : "query";
2203 
2204   snprintf(buffer, HUGE_STRING_LENGTH,
2205            "%s,%s,%ld.%03ld,%ld.%03ld,%ld.%03ld,%d,%llu\n",
2206            con->engine ? con->engine : "", /* Storage engine we ran against */
2207            ptr, /* Load type */
2208            con->avg_timing / 1000, con->avg_timing % 1000, /* Time to load */
2209            con->min_timing / 1000, con->min_timing % 1000, /* Min time */
2210            con->max_timing / 1000, con->max_timing % 1000, /* Max time */
2211            con->users, /* Children used */
2212            con->avg_rows  /* Queries run */
2213           );
2214   my_write(csv_file, (uchar*) buffer, (uint)strlen(buffer), MYF(0));
2215 }
2216 
2217 void
generate_stats(conclusions * con,option_string * eng,stats * sptr)2218 generate_stats(conclusions *con, option_string *eng, stats *sptr)
2219 {
2220   stats *ptr;
2221   unsigned int x;
2222 
2223   con->min_timing= sptr->timing;
2224   con->max_timing= sptr->timing;
2225   con->min_rows= sptr->rows;
2226   con->max_rows= sptr->rows;
2227 
2228   /* At the moment we assume uniform */
2229   con->users= sptr->users;
2230   con->avg_rows= sptr->rows;
2231 
2232   /* With no next, we know it is the last element that was malloced */
2233   for (ptr= sptr, x= 0; x < iterations; ptr++, x++)
2234   {
2235     con->avg_timing+= ptr->timing;
2236 
2237     if (ptr->timing > con->max_timing)
2238       con->max_timing= ptr->timing;
2239     if (ptr->timing < con->min_timing)
2240       con->min_timing= ptr->timing;
2241   }
2242   con->avg_timing= con->avg_timing/iterations;
2243 
2244   if (eng && eng->string)
2245     con->engine= eng->string;
2246   else
2247     con->engine= NULL;
2248 }
2249 
2250 void
option_cleanup(option_string * stmt)2251 option_cleanup(option_string *stmt)
2252 {
2253   option_string *ptr, *nptr;
2254   if (!stmt)
2255     return;
2256 
2257   for (ptr= stmt; ptr; ptr= nptr)
2258   {
2259     nptr= ptr->next;
2260     my_free(ptr->string);
2261     my_free(ptr->option);
2262     my_free(ptr);
2263   }
2264 }
2265 
2266 void
statement_cleanup(statement * stmt)2267 statement_cleanup(statement *stmt)
2268 {
2269   statement *ptr, *nptr;
2270   if (!stmt)
2271     return;
2272 
2273   for (ptr= stmt; ptr; ptr= nptr)
2274   {
2275     nptr= ptr->next;
2276     my_free(ptr->string);
2277     my_free(ptr);
2278   }
2279 }
2280 
2281 
2282 int
slap_connect(MYSQL * mysql)2283 slap_connect(MYSQL *mysql)
2284 {
2285   /* Connect to server */
2286   static ulong connection_retry_sleep= 100000; /* Microseconds */
2287   int x, connect_error= 1;
2288   for (x= 0; x < 10; x++)
2289   {
2290     set_mysql_connect_options(mysql);
2291     if (opt_init_command)
2292       mysql_options(mysql, MYSQL_INIT_COMMAND, opt_init_command);
2293     if (mysql_real_connect(mysql, host, user, opt_password,
2294                            create_schema_string,
2295                            opt_mysql_port,
2296                            opt_mysql_unix_port,
2297                            connect_flags))
2298     {
2299       /* Connect succeeded */
2300       connect_error= 0;
2301       break;
2302     }
2303     my_sleep(connection_retry_sleep);
2304   }
2305   if (connect_error)
2306   {
2307     fprintf(stderr,"%s: Error when connecting to server: %d %s\n",
2308             my_progname, mysql_errno(mysql), mysql_error(mysql));
2309     return 1;
2310   }
2311 
2312   return 0;
2313 }
2314