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