summaryrefslogtreecommitdiff
path: root/client/mysqlslap.c
diff options
context:
space:
mode:
Diffstat (limited to 'client/mysqlslap.c')
-rw-r--r--client/mysqlslap.c1162
1 files changed, 1162 insertions, 0 deletions
diff --git a/client/mysqlslap.c b/client/mysqlslap.c
new file mode 100644
index 00000000000..5e280b332bf
--- /dev/null
+++ b/client/mysqlslap.c
@@ -0,0 +1,1162 @@
+/* Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ coded by: Patrick Galbraith
+*/
+
+
+/*
+
+ MySQL Slap
+
+ A simple program designed to work as if multiple clients querying the database,
+ then reporting the timing of each stage.
+
+ MySQL slap runs three stages:
+ 1) Create Schema (single client)
+ 2) Insert data (many clients)
+ 3) Load test (many clients)
+ 4) Cleanup (disconnection, drop table if specified, single client)
+
+Examples:
+
+Supply your own create, insert and query SQL statements, with 5 clients
+loading (10 inserts for each), 50 clients querying:
+
+./mysqlslap --create="CREATE TABLE A (a int)" --data="INSERT INTO A (23)" --iterations=200
+ --query="SELECT * FROM A" --number-rows=10 --concurrency=50 --load-concurrency=5
+
+Let the program build create, insert and query SQL statements with a table of
+two int columns, 3 varchar columns, with 5 clients loading data
+(12 inserts each), 5 clients querying, drop schema before creating:
+
+./mysqlslap --concurrency=5 --concurrency-load=5 --iterations=20 --number-int-cols=2 --number-char-cols=3 --number-rows=12 --auto-generate-sql
+
+Let the program build the query SQL statement with a table of
+two int columns, 3 varchar columns, 5 clients querying, don't
+create the table or insert the data (using the previous test's schema
+and data)
+
+./mysqlslap --concurrency=5 --iterations=20 --number-int-cols=2 --number-char-cols=3 --number-rows=12 --auto-generate-sql --skip-data-load --skip-create-schema
+
+Tell the program to load the create, insert and query SQL statements
+from the specified files which the create.sql file has multiple table creation
+statements delimited by ';', multiple insert statements delimited by ';',
+and multiple queries delimited by ';', run all the load statements with 5
+clients, 5 times per client, and run all the queries in the query file with
+5 clients:
+
+/mysqlslap --drop-schema --concurrency=5 --concurrency-load=5 --iterations=5 --query=query.sql --create=create.sql --data=insert.sql --delimiter=";" --number-rows=5
+
+Same as the last test run, with short options
+./mysqlslap -D -c 5 -l 5 -i 5 -q query.sql --create create.sql -d insert.sql -F ";" -n 5
+
+*/
+
+#define SHOW_VERSION "0.1"
+
+#define HUGE_STRING_LENGTH 8096
+#define RAND_STRING_SIZE 126
+
+#include "client_priv.h"
+#include <my_sys.h>
+#include <m_string.h>
+#include <mysql.h>
+#include <mysqld_error.h>
+#include <my_dir.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <sslopt-vars.h>
+#include <sys/time.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+
+static int drop_schema(MYSQL *mysql, const char *db);
+unsigned int get_random_number(void);
+unsigned int get_random_string(char *buf);
+static int build_table_string(void);
+static int build_insert_string(void);
+static int build_query_string(void);
+static int create_schema(MYSQL *mysql, const char *db,const char *script);
+static int run_scheduler(const char *script, int(*task)(const char *),
+ unsigned int concur);
+int run_task(const char *script);
+int load_data(const char *script);
+
+static char *host= NULL, *opt_password= NULL, *user= NULL,
+ *user_supplied_query= NULL, *user_supplied_data= NULL,
+ *create_string= NULL, *default_engine= NULL, *delimiter= NULL,
+ *opt_mysql_unix_port= NULL;
+
+static my_bool opt_compress= FALSE, opt_count= FALSE, tty_password= FALSE,
+ opt_drop= FALSE, create_string_alloced= FALSE,
+ insert_string_alloced= FALSE, query_string_alloced= FALSE,
+ generated_insert_flag= FALSE, opt_silent= FALSE,
+ auto_generate_sql= FALSE, opt_skip_create= FALSE,
+ opt_skip_data_load= FALSE, opt_skip_query= FALSE;
+
+static int verbose= 0, num_int_cols=-1, num_char_cols=-1, delimiter_length= 0;
+static char *default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME;
+static unsigned int number_of_rows= 1000, number_of_iterations= 1000,
+ concurrency= 1, concurrency_load= 1, children_spawned= 1;
+
+const char *default_dbug_option="d:t:o,/tmp/mysqlslap.trace";
+
+static uint opt_protocol= 0;
+
+static void get_options(int *argc,char ***argv);
+static uint opt_mysql_port= 0;
+
+static const char *load_default_groups[]= { "mysqlslap","client",0 };
+#if ELEGANT_SOLUTION
+static const char ALPHANUMERICS[]=
+ "0123456789ABCDEFGHIJKLMNOPQRSTWXYZabcdefghijklmnopqrstuvwxyz";
+static const ALPHANUMERICS_SIZE= (sizeof(ALPHANUMERICS)-1);
+#endif
+
+/* returns the time in ms between two timevals */
+
+static double timedif(struct timeval end, struct timeval begin)
+{
+ double seconds;
+ DBUG_ENTER("timedif");
+
+ seconds= (double)(end.tv_usec - begin.tv_usec)/1000000;
+ DBUG_PRINT("info", ("end.tv_usec %d - begin.tv_usec %d = %d microseconds ( fseconds %f)",
+ end.tv_usec, begin.tv_usec,
+ (end.tv_usec - begin.tv_usec),
+ seconds));
+ seconds += (double)(end.tv_sec - begin.tv_sec);
+ DBUG_PRINT("info", ("end.tv_sec %d - begin.tv_sec %d = %d seconds (fseconds %f)",
+ end.tv_sec, begin.tv_sec,
+ (end.tv_sec - begin.tv_sec), seconds));
+
+ DBUG_PRINT("info", ("returning time %f seconds", seconds));
+ DBUG_RETURN(seconds);
+}
+
+
+int main(int argc, char **argv)
+{
+ my_bool first_argument_uses_wildcards= 0;
+ char *wild;
+ MYSQL mysql;
+ int client_flag= 0;
+ double time_difference;
+ struct timeval tmp_time, start_time, load_time, run_time;
+ /* for the string generation later */
+
+ DBUG_ENTER("main");
+ MY_INIT(argv[0]);
+
+ /* for any string generation later on */
+ if (auto_generate_sql)
+ srandom((unsigned int) tmp_time.tv_usec);
+
+ bzero(&load_time, sizeof(struct timeval));
+ bzero(&run_time, sizeof(struct timeval));
+ load_defaults("my",load_default_groups,&argc,&argv);
+ get_options(&argc,&argv);
+ /* globals? Yes, so we only have to run strlen once */
+ if (delimiter)
+ delimiter_length= strlen(delimiter);
+
+ wild= 0;
+ if (argc)
+ {
+ char *pos= argv[argc-1], *to;
+ for (to= pos ; *pos ; pos++, to++)
+ {
+ switch (*pos) {
+ case '*':
+ *pos= '%';
+ first_argument_uses_wildcards= 1;
+ break;
+ case '?':
+ *pos= '_';
+ first_argument_uses_wildcards= 1;
+ break;
+ case '%':
+ case '_':
+ first_argument_uses_wildcards= 1;
+ break;
+ case '\\':
+ pos++;
+ default: break;
+ }
+ *to= *pos;
+ }
+ *to= *pos; /* just to copy a '\0' if '\\' was used */
+ }
+ if (first_argument_uses_wildcards)
+ wild= argv[--argc];
+ else if (argc == 3) /* We only want one field */
+ wild= argv[--argc];
+
+ if (argc > 2)
+ {
+ fprintf(stderr,"%s: Too many arguments\n",my_progname);
+ exit(1);
+ }
+ mysql_init(&mysql);
+ if (opt_compress)
+ mysql_options(&mysql,MYSQL_OPT_COMPRESS,NullS);
+#ifdef HAVE_OPENSSL
+ if (opt_use_ssl)
+ mysql_ssl_set(&mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
+ opt_ssl_capath, opt_ssl_cipher);
+#endif
+ if (opt_protocol)
+ mysql_options(&mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
+#ifdef HAVE_SMEM
+ if (shared_memory_base_name)
+ mysql_options(&mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name);
+#endif
+ mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset);
+
+ client_flag|= CLIENT_MULTI_RESULTS;
+ if (!(mysql_real_connect(&mysql,host,user,opt_password,
+ (first_argument_uses_wildcards) ? "" :
+ argv[0],opt_mysql_port,opt_mysql_unix_port,
+ client_flag)))
+ {
+ fprintf(stderr,"%s: %s\n",my_progname,mysql_error(&mysql));
+ exit(1);
+ }
+
+ /*
+ we might not want to load any data, if for instance we are calling
+ a stored_procedure that doesn't use data, or we know we already have
+ data in the table
+ */
+ if (opt_drop)
+ drop_schema(&mysql, "mysqlslap");
+
+ if (!opt_skip_create)
+ create_schema(&mysql, "mysqlslap", create_string);
+
+ if (!opt_skip_data_load)
+ {
+ gettimeofday(&start_time, NULL);
+ DBUG_PRINT("info", ("user_supplied_data %s", user_supplied_data));
+ run_scheduler(user_supplied_data, load_data, concurrency_load);
+ gettimeofday(&load_time, NULL);
+ time_difference= timedif(load_time, start_time);
+ if (!opt_silent)
+ {
+ printf("Seconds to load data: %.5f\n",time_difference);
+ printf("Number of clients loading data: %d\n", children_spawned);
+ printf("Number of inserts per client: %d\n", number_of_rows);
+ }
+ }
+
+ if (!opt_skip_query)
+ {
+ run_scheduler(user_supplied_query, run_task, concurrency);
+ gettimeofday(&run_time, 0);
+ time_difference= timedif(run_time, load_time);
+
+ if (!opt_silent)
+ {
+ printf("Seconds to run all queries: %.5f\n", time_difference);
+ printf("Number of clients running queries: %d\n", children_spawned);
+ printf("Number of queries per client: %d\n", number_of_iterations);
+ }
+ }
+ if (opt_drop)
+ drop_schema(&mysql, "mysqlslap");
+
+ mysql_close(&mysql); /* Close & free connection */
+
+ /* now free all the strings we created */
+ if (opt_password)
+ my_free(opt_password,MYF(0));
+ if (create_string_alloced)
+ my_free(create_string,MYF(0));
+ if (insert_string_alloced)
+ my_free(user_supplied_data,MYF(0));
+ if (query_string_alloced)
+ my_free(user_supplied_query,MYF(0));
+#ifdef HAVE_SMEM
+ if (shared_memory_base_name)
+ my_free(shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR));
+#endif
+ my_end(0);
+
+ DBUG_RETURN(0); /* No compiler warnings */
+}
+
+static struct my_option my_long_options[] =
+{
+ {"auto-generate-sql", 'a', "Generate SQL where not supplied by file or command line.",
+ (gptr*) &auto_generate_sql, (gptr*) &auto_generate_sql,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"compress", 'C', "Use compression in server/client protocol.",
+ (gptr*) &opt_compress, (gptr*) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
+ 0, 0, 0},
+ {"concurrency-load", 'l', "Number of clients to simulate for data load.",
+ (gptr*) &concurrency_load, (gptr*) &concurrency_load, 0,
+ GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"concurrency", 'c', "Number of clients to simulate for query to run.",
+ (gptr*) &concurrency, (gptr*) &concurrency, 0, GET_UINT,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"create", OPT_CREATE_SLAP_SCHEMA, "File or string to use for create.",
+ (gptr*) &create_string, (gptr*) &create_string, 0, GET_STR, REQUIRED_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"data", 'd',
+ "File or string with INSERT to use for populating table(s).",
+ (gptr*) &user_supplied_data, (gptr*) &user_supplied_data, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
+ (gptr*) &default_dbug_option, (gptr*) &default_dbug_option, 0, GET_STR,
+ OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"delimiter", 'F', "Delimiter to use in SQL statements supplied in file or command line.",
+ (gptr*) &delimiter, (gptr*) &delimiter, 0, GET_STR, REQUIRED_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"drop-schema", 'D',
+ "Drop schema if it exists prior to running, and after running",
+ (gptr*) &opt_drop, (gptr*) &opt_drop, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"engine", 'e', "Storage engine to use for creating the table.",
+ (gptr*) &default_engine, (gptr*) &default_engine, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"host", 'h', "Connect to host.", (gptr*) &host, (gptr*) &host, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"iterations", 'i', "Number of iterations.", (gptr*) &number_of_iterations,
+ (gptr*) &number_of_iterations, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"number-char-cols", 'x', "Number of INT columns to create table with if specifying --sql-generate-sql.",
+ (gptr*) &num_char_cols, (gptr*) &num_char_cols, 0, GET_UINT, REQUIRED_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"number-int-cols", 'y', "Number of VARCHAR columns to create table with if specifying --sql-generate-sql.",
+ (gptr*) &num_int_cols, (gptr*) &num_int_cols, 0, GET_UINT, REQUIRED_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"number-rows", 'n', "Number of rows to insert when loading data.", (gptr*) &number_of_rows,
+ (gptr*) &number_of_rows, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"password", 'p',
+ "Password to use when connecting to server. If password is not given it's asked from the tty.",
+ 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"port", 'P', "Port number to use for connection.", (gptr*) &opt_mysql_port,
+ (gptr*) &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0,
+ 0},
+#ifdef __WIN__
+ {"pipe", 'W', "Use named pipes to connect to server.", 0, 0, 0, GET_NO_ARG,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+#endif
+ {"protocol", OPT_MYSQL_PROTOCOL,
+ "The protocol of connection (tcp,socket,pipe,memory).",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"skip-create-schema", 'A', "Don't create a schema, use existing schema.",
+ (gptr*) &opt_skip_create, (gptr*) &opt_skip_create, 0, GET_BOOL, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"skip-data-load", 'L', "Don't load any data, use existing data set.",
+ (gptr*) &opt_skip_data_load, (gptr*) &opt_skip_data_load, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"skip-query", 'Q', "Don't run any queries.",
+ (gptr*) &opt_skip_query, (gptr*) &opt_skip_query, 0, GET_BOOL, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"silent", 's', "Run program in silent mode - no output.",
+ (gptr*) &opt_silent, (gptr*) &opt_silent, 0, GET_BOOL, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
+#ifdef HAVE_SMEM
+ {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME,
+ "Base name of shared memory.", (gptr*) &shared_memory_base_name,
+ (gptr*) &shared_memory_base_name, 0, GET_STR_ALLOC, REQUIRED_ARG,
+ 0, 0, 0, 0, 0, 0},
+#endif
+ {"query", 'q', "Query to run or file containing query to run.",
+ (gptr*) &user_supplied_query, (gptr*) &user_supplied_query,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"socket", 'S', "Socket file to use for connection.",
+ (gptr*) &opt_mysql_unix_port, (gptr*) &opt_mysql_unix_port, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#include <sslopt-longopts.h>
+#ifndef DONT_ALLOW_USER_CHANGE
+ {"user", 'u', "User for login if not current user.", (gptr*) &user,
+ (gptr*) &user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#endif
+ {"verbose", 'v',
+ "More verbose output; You can use this multiple times to get even more verbose output.",
+ (gptr*) &verbose, (gptr*) &verbose, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+
+#include <help_start.h>
+
+static void print_version(void)
+{
+ printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname,SHOW_VERSION,
+ MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE);
+}
+
+
+static void usage(void)
+{
+ print_version();
+ puts("Copyright (C) 2005 MySQL AB");
+ puts("This software comes with ABSOLUTELY NO WARRANTY. This is free software,\
+ \nand you are welcome to modify and redistribute it under the GPL \
+ license\n");
+ puts("Run a query multiple times against the server\n");
+ printf("Usage: %s [OPTIONS] [database [table [column]]]\n",my_progname);
+ print_defaults("my",load_default_groups);
+ my_print_help(my_long_options);
+}
+
+#include <help_end.h>
+
+static my_bool
+get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
+ char *argument)
+{
+ DBUG_ENTER("get_one_option");
+ switch(optid) {
+#ifdef __NETWARE__
+ case OPT_AUTO_CLOSE:
+ setscreenmode(SCR_AUTOCLOSE_ON_EXIT);
+ break;
+#endif
+ case 'v':
+ verbose++;
+ break;
+ case 'p':
+ if (argument)
+ {
+ char *start= argument;
+ my_free(opt_password,MYF(MY_ALLOW_ZERO_PTR));
+ opt_password= my_strdup(argument,MYF(MY_FAE));
+ while (*argument) *argument++= 'x'; /* Destroy argument */
+ if (*start)
+ start[1]= 0; /* Cut length of argument */
+ tty_password= 0;
+ }
+ else
+ tty_password= 1;
+ break;
+ case 'W':
+#ifdef __WIN__
+ opt_protocol= MYSQL_PROTOCOL_PIPE;
+#endif
+ break;
+ case OPT_MYSQL_PROTOCOL:
+ {
+ if ((opt_protocol= find_type(argument, &sql_protocol_typelib,0)) <= 0)
+ {
+ fprintf(stderr, "Unknown option to protocol: %s\n", argument);
+ exit(1);
+ }
+ break;
+ }
+ case '#':
+ DBUG_PUSH(argument ? argument : default_dbug_option);
+ break;
+#include <sslopt-case.h>
+ case 'V':
+ print_version();
+ exit(0);
+ break;
+ case '?':
+ case 'I': /* Info */
+ usage();
+ exit(0);
+ }
+ DBUG_RETURN(0);
+}
+
+/*
+ get_random_number()
+
+ returns a random number to be used in building an sql statement
+*/
+unsigned int
+get_random_number(void)
+{
+ int num;
+ char rstate[8];
+ struct timeval timestruct;
+
+ DBUG_ENTER("get_random_number");
+ gettimeofday(&timestruct, NULL);
+ initstate(timestruct.tv_usec, rstate, sizeof(rstate));
+ setstate(rstate);
+ num= random();
+ DBUG_RETURN(num);
+}
+
+unsigned int
+get_random_string(char *buf)
+{
+ char *buf_ptr= buf;
+ int x, chr= 0;
+
+ DBUG_ENTER("get_random_string");
+ for (x= RAND_STRING_SIZE; x > 0; x--)
+ {
+ /*
+ why this tmp_num? Because for some reason, x's value gets
+ munged during call of random()
+ */
+ int tmp_num= x;
+ chr= random()% tmp_num;
+ /*
+ avoiding problematic chars for insert
+ */
+ if (chr >= 48 && chr <= 122 && chr != 96 && chr != 92)
+ *buf_ptr++= chr;
+ }
+
+ /*
+ strlen
+ */
+ DBUG_RETURN(buf_ptr- buf);
+}
+#if ELEGANT_SOLUTION
+/*
+ I would like to use this, but for some reason, subsequent calls to
+ this function result in buf having non-alpha characters added beyond it's
+ size, which in turn corrupt the insert string.
+*/
+unsigned int
+get_random_string(char *buf)
+{
+ char *buf_ptr=buf;
+ int x= RAND_STRING_SIZE;
+
+ DBUG_ENTER("get_random_string");
+ DBUG_PRINT("info", ("RANDOM STRING buf %s length %d", buf, buf_ptr - buf));
+ while(x--)
+ {
+ int tmp_x=x;
+ *buf_ptr++= ALPHANUMERICS[random() % ALPHANUMERICS_SIZE];
+ x= tmp_x;
+ //fprintf(stderr, "get_random_string %s %lx x %d\n", buf_ptr, buf_ptr,
+ //tmp_x);
+ }
+ DBUG_PRINT("info", ("RANDOM STRING buf %s length %d", buf, buf_ptr - buf));
+
+ /* strlen */
+ DBUG_RETURN(buf_ptr- buf);
+}
+#endif
+/*
+ build_table_string
+
+ This function builds a create table query if the user opts to not supply
+ a file or string containing a create table statement
+*/
+static int
+build_table_string(void)
+{
+ char buf[512];
+ int col_count;
+ DYNAMIC_STRING table_string;
+ DBUG_ENTER("build_table_string");
+
+ DBUG_PRINT("info", ("num int cols %d num char cols %d",
+ num_int_cols, num_char_cols));
+
+ init_dynamic_string(&table_string, "", 1024, 1024);
+
+ dynstr_append_mem(&table_string, "CREATE TABLE `t1` (\n", 20);
+ for (col_count= 1; col_count <= num_int_cols; col_count++)
+ {
+ sprintf(buf, "intcol%d INT(32)", col_count);
+ dynstr_append(&table_string, buf);
+
+ if (col_count < num_int_cols || num_char_cols > 0)
+ dynstr_append_mem(&table_string, ",", 1);
+
+ dynstr_append_mem(&table_string, "\n", 1);
+ }
+ for (col_count= 1; col_count <= num_char_cols; col_count++)
+ {
+ sprintf(buf, "charcol%d VARCHAR(128)", col_count);
+ dynstr_append(&table_string, buf);
+
+ if (col_count < num_char_cols)
+ dynstr_append_mem(&table_string, ",", 1);
+
+ dynstr_append_mem(&table_string, "\n", 1);
+ }
+ dynstr_append_mem(&table_string, ")\n", 2);
+ create_string= (char *)my_malloc(table_string.length+1, MYF(MY_WME));
+ create_string_alloced= 1;
+ strmov(create_string, table_string.str);
+ DBUG_PRINT("info", ("create_string %s", create_string));
+ dynstr_free(&table_string);
+ DBUG_RETURN(0);
+}
+
+/*
+ build_insert_string()
+
+ This function builds insert statements when the user opts to not supply
+ a insert file or string containing insert data
+*/
+static int
+build_insert_string(void)
+{
+ char buf[RAND_STRING_SIZE];
+ int col_count;
+ DYNAMIC_STRING insert_string;
+ MYSQL mysql;
+ DBUG_ENTER("build_insert_string");
+ mysql_init(&mysql);
+
+ init_dynamic_string(&insert_string, "", 1024, 1024);
+
+ dynstr_append_mem(&insert_string, "INSERT INTO t1 VALUES (", 23);
+ for (col_count= 1; col_count <= num_int_cols; col_count++)
+ {
+ sprintf(buf, "%d", get_random_number());
+ dynstr_append(&insert_string, buf);
+
+ if (col_count < num_int_cols || num_char_cols > 0)
+ dynstr_append_mem(&insert_string, ",", 1);
+ }
+ for (col_count= 1; col_count <= num_char_cols; col_count++)
+ {
+ char *buf_tmp= (char *) my_malloc(RAND_STRING_SIZE, MYF(MY_WME));
+ int buf_len;
+ buf_len= get_random_string((char *) &buf);
+ mysql_real_escape_string(&mysql, buf_tmp, (char *) &buf, buf_len);
+ dynstr_append_mem(&insert_string, "'", 1);
+ dynstr_append_mem(&insert_string, buf_tmp, buf_len);
+ dynstr_append_mem(&insert_string, "'", 1);
+ my_free(buf_tmp, MYF(0));
+
+ if (col_count < num_char_cols)
+ dynstr_append_mem(&insert_string, ",", 1);
+
+ }
+ dynstr_append_mem(&insert_string, ")", 1);
+
+ /*
+ since this function can be called if the user wants varying insert
+ statement in the for loop where inserts run, free in advance
+ */
+ if (insert_string_alloced)
+ my_free(user_supplied_data,MYF(0));
+ user_supplied_data= (char *)my_malloc(insert_string.length+1, MYF(MY_WME));
+ insert_string_alloced= 1;
+ strmov(user_supplied_data, insert_string.str);
+ DBUG_PRINT("info", ("generated_insert_data %s", user_supplied_data));
+ dynstr_free(&insert_string);
+ mysql_close(&mysql);
+ DBUG_RETURN(insert_string.length+1);
+}
+
+/*
+ build_query_string()
+
+ This function builds a query if the user opts to not supply a query
+ statement or file containing a query statement
+*/
+static int
+build_query_string(void)
+{
+ char buf[512];
+ int col_count;
+ static DYNAMIC_STRING query_string;
+ DBUG_ENTER("build_query_string");
+
+ init_dynamic_string(&query_string, "", 1024, 1024);
+
+ dynstr_append_mem(&query_string, "SELECT ", 7);
+ for (col_count= 1; col_count <= num_int_cols; col_count++)
+ {
+ sprintf(buf, "intcol%d", col_count);
+ dynstr_append(&query_string, buf);
+
+ if (col_count < num_int_cols || num_char_cols > 0)
+ dynstr_append_mem(&query_string, ",", 1);
+
+ }
+ for (col_count= 1; col_count <= num_char_cols; col_count++)
+ {
+ sprintf(buf, "charcol%d", col_count);
+ dynstr_append(&query_string, buf);
+
+ if (col_count < num_char_cols)
+ dynstr_append_mem(&query_string, ",", 1);
+
+ }
+ dynstr_append_mem(&query_string, " FROM t1", 8);
+ user_supplied_query= (char *)my_malloc(query_string.length+1, MYF(MY_WME));
+ query_string_alloced= 1;
+ strmov(user_supplied_query, query_string.str);
+ DBUG_PRINT("info", ("user_supplied_query %s", user_supplied_query));
+ dynstr_free(&query_string);
+ DBUG_RETURN(0);
+}
+
+static void
+get_options(int *argc,char ***argv)
+{
+ int ho_error;
+ MY_STAT sbuf; /* Stat information for the data file */
+
+ DBUG_ENTER("get_options");
+ if ((ho_error= handle_options(argc, argv, my_long_options, get_one_option)))
+ exit(ho_error);
+
+ /*
+ Default policy - if they don't supply either char or int cols, and
+ also no data, then default to 1 of each.
+ */
+ if (num_int_cols == 0 && num_char_cols == 0 && auto_generate_sql &&
+ !user_supplied_data)
+ {
+ num_int_cols= 1;
+ num_char_cols= 1;
+ }
+
+ if (!default_engine)
+ default_engine= (char *)"MYISAM";
+
+ if (!user)
+ user= (char *)"root";
+
+ if(auto_generate_sql && create_string &&
+ user_supplied_data && user_supplied_query)
+ {
+ fprintf(stderr,
+ "%s: Can't use --auto-generate-sql when create, insert, and query strings are specified!\n",
+ my_progname);
+ exit(1);
+ }
+
+ if (opt_skip_create && opt_drop)
+ {
+ fprintf(stderr,"You cannot specify to drop the schema and skip schema creation!\n");
+ exit(1);
+ }
+
+ if (!opt_skip_create)
+ {
+ if (auto_generate_sql && !create_string)
+ build_table_string();
+ else if (create_string && my_stat(create_string, &sbuf, MYF(0)))
+ {
+ File data_file;
+ if (!MY_S_ISREG(sbuf.st_mode))
+ {
+ fprintf(stderr,"%s: Create file was not a regular file\n",
+ my_progname);
+ exit(1);
+ }
+ if ((data_file= my_open(create_string, O_RDWR, MYF(0))) == -1)
+ {
+ fprintf(stderr,"%s: Could not open create file\n", my_progname);
+ exit(1);
+ }
+ create_string= (char *)my_malloc(sbuf.st_size+1, MYF(MY_WME));
+ create_string_alloced= 1;
+ my_read(data_file, create_string, sbuf.st_size, MYF(0));
+ create_string[sbuf.st_size]= '\0';
+ my_close(data_file,MYF(0));
+ }
+ else if (!auto_generate_sql && !create_string)
+ {
+ fprintf(stderr,"%s: Must use --auto-generate-sql or --create option\n",
+ my_progname);
+ exit(1);
+ }
+ }
+
+ if (opt_skip_data_load)
+ {
+ if (user_supplied_data)
+ fprintf(stderr,
+ "Warning: Specified data to INSERT, but data load is disabled.\n");
+ }
+ else
+ {
+ if (!user_supplied_data && auto_generate_sql)
+ {
+ int length;
+ generated_insert_flag= 1;
+ length= build_insert_string();
+ DBUG_PRINT("info", ("user_supplied_data is %s", user_supplied_data));
+ }
+ else if (my_stat(user_supplied_data, &sbuf, MYF(0)))
+ {
+ File data_file;
+ if (!MY_S_ISREG(sbuf.st_mode))
+ {
+ fprintf(stderr,"%s: User data supplied file was not a regular file\n",
+ my_progname);
+ exit(1);
+ }
+ if ((data_file= my_open(user_supplied_data, O_RDWR, MYF(0))) == -1)
+ {
+ fprintf(stderr,"%s: Could not open data supplied file\n", my_progname);
+ exit(1);
+ }
+ user_supplied_data= (char *)my_malloc(sbuf.st_size+1, MYF(MY_WME));
+ insert_string_alloced= 1;
+ my_read(data_file, user_supplied_data, sbuf.st_size, MYF(0));
+ user_supplied_data[sbuf.st_size]= '\0';
+ my_close(data_file,MYF(0));
+ }
+ else if (!user_supplied_query && !auto_generate_sql)
+ {
+ fprintf(stderr,"%s: No user supplied data to insert or --auto-generate-sql\
+ specified!\n", my_progname);
+ exit(1);
+ }
+ }
+
+ if (!opt_skip_query)
+ {
+ if (!user_supplied_query && auto_generate_sql)
+ {
+ build_query_string();
+ }
+ else if (my_stat(user_supplied_query, &sbuf, MYF(0)))
+ {
+ File data_file;
+ if (!MY_S_ISREG(sbuf.st_mode))
+ {
+ fprintf(stderr,"%s: User query supplied file was not a regular file\n",
+ my_progname);
+ exit(1);
+ }
+ if ((data_file= my_open(user_supplied_query, O_RDWR, MYF(0))) == -1)
+ {
+ fprintf(stderr,"%s: Could not open query supplied file\n", my_progname);
+ exit(1);
+ }
+ user_supplied_query= (char *)my_malloc(sbuf.st_size+1, MYF(MY_WME));
+ query_string_alloced= 1;
+ my_read(data_file, user_supplied_query, sbuf.st_size, MYF(0));
+ user_supplied_query[sbuf.st_size]= '\0';
+ my_close(data_file,MYF(0));
+ }
+ else if (!user_supplied_query && !auto_generate_sql)
+ {
+ fprintf(stderr,"%s: No user supplied query or --auto-generate-sql\
+ specified!\n", my_progname);
+ exit(1);
+ }
+ }
+
+ if (tty_password)
+ opt_password= get_tty_password(NullS);
+ if (opt_count)
+ {
+ /*
+ We need to set verbose to 2 as we need to change the output to include
+ the number-of-rows column
+ */
+ verbose= 2;
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+static int
+create_schema(MYSQL *mysql,const char *db,const char *script)
+{
+ char query[HUGE_STRING_LENGTH], buffer[HUGE_STRING_LENGTH];
+
+ DBUG_ENTER("create_schema");
+ snprintf(query, HUGE_STRING_LENGTH, "DROP SCHEMA IF EXISTS `%s`", db);
+ DBUG_PRINT("info", ("query %s", query));
+ mysql_query(mysql, query);
+
+ snprintf(query, HUGE_STRING_LENGTH, "CREATE SCHEMA `%s`", db);
+ DBUG_PRINT("info", ("query %s", query));
+ if (mysql_query(mysql, query))
+ {
+ fprintf(stderr,"%s: Cannot create schema %s : %s\n", my_progname, db,
+ mysql_error(mysql));
+ exit(1);
+ }
+
+ if (mysql_select_db(mysql,db))
+ {
+ fprintf(stderr,"%s: Cannot select schema '%s': %s\n",my_progname, db,
+ mysql_error(mysql));
+ exit(1);
+ }
+
+ snprintf(buffer, HUGE_STRING_LENGTH, "set storage_engine=`%s`",
+ default_engine);
+ if (mysql_query(mysql, buffer))
+ {
+ fprintf(stderr,"%s: Cannot set default engine: %s\n", my_progname,
+ mysql_error(mysql));
+ exit(1);
+ }
+
+ if (delimiter)
+ {
+ char *retstr;
+ char buf[HUGE_STRING_LENGTH];
+
+ while((retstr= strstr(script, delimiter)))
+ {
+ strncpy((char*)&buf, script, retstr - script);
+ buf[retstr - script]= '\0';
+ script+= retstr - script + delimiter_length;
+ DBUG_PRINT("info", ("running create QUERY %s", (char *)buf));
+ if (mysql_query(mysql, buf))
+ {
+ fprintf(stderr,"%s: Cannot run query %s ERROR : %s\n",
+ my_progname, (char *)buf, mysql_error(mysql));
+ exit(1);
+ }
+ }
+ }
+ /*
+ remainder, if there was a delimeter, or the whole query if not a
+ delimiter. If less than 3, can't be anything useful
+ */
+ if ((strlen(script)) < 3)
+ DBUG_RETURN(0);
+ if (mysql_query(mysql, script))
+ {
+ fprintf(stderr,"%s: Cannot create tables: %s\n", my_progname,
+ mysql_error(mysql));
+ exit(1);
+ }
+
+ DBUG_RETURN(0);
+}
+
+static int
+drop_schema(MYSQL *mysql,const char *db)
+{
+ char query[HUGE_STRING_LENGTH];
+
+ DBUG_ENTER("drop_schema");
+ snprintf(query, HUGE_STRING_LENGTH, "DROP SCHEMA IF EXISTS `%s`", db);
+ if (mysql_query(mysql, query))
+ {
+ fprintf(stderr,"%s: Cannot drop database '%s' ERROR : %s\n",
+ my_progname, db, mysql_error(mysql));
+ exit(1);
+ }
+
+ DBUG_RETURN(0);
+}
+
+
+static int
+run_scheduler(const char *script,
+ int(*task)(const char *), unsigned int concur)
+{
+ uint x;
+
+ DBUG_ENTER("run_scheduler");
+ /*
+ reset to 0
+ */
+ children_spawned= 0;
+
+ for (x= 0; x < concur; x++)
+ {
+ int pid;
+ DBUG_PRINT("info", ("x %d concurrency %d", x, concurrency));
+ pid= fork();
+ switch(pid)
+ {
+ case 0:
+ /* child */
+ DBUG_PRINT("info", ("fork returned 0, calling task(\"%s\"), pid %d gid %d",
+ script, pid, getgid()));
+ if (verbose >= 2)
+ fprintf(stderr,
+ "%s: fork returned 0, calling task(\"%s\") pid %d gid %d\n",
+ my_progname, script, pid, getgid());
+ task(script);
+ exit(0);
+ break;
+ case -1:
+ /* error */
+ DBUG_PRINT("info",
+ ("fork returned -1, failing pid %d gid %d", pid, getgid()));
+ fprintf(stderr,
+ "%s: Failed on fork: -1, max procs per parent exceeded.\n",
+ my_progname);
+ /*exit(1);*/
+ goto WAIT;
+ default:
+ /* parent, forked */
+ DBUG_PRINT("info", ("default, break: pid %d gid %d", pid, getgid()));
+ if (verbose >= 2)
+ fprintf(stderr,"%s: fork returned %d, gid %d\n",
+ my_progname, pid, getgid());
+ break;
+ }
+ children_spawned++;
+ }
+
+WAIT:
+ while (x--)
+ {
+ int status, pid;
+ pid= wait(&status);
+ DBUG_PRINT("info", ("Parent: child %d status %d", pid, status));
+ }
+
+ DBUG_RETURN(0);
+}
+
+int
+run_task(const char *script)
+{
+ uint counter= 0, x;
+ MYSQL mysql;
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+
+ DBUG_ENTER("run_task");
+ DBUG_PRINT("info", ("task script \"%s\"", script));
+
+ mysql_init(&mysql);
+
+ DBUG_PRINT("info", ("trying to connect to host %s as user %s", host, user));
+ if (!(mysql_real_connect(&mysql, host, user, opt_password,
+ "mysqlslap", opt_mysql_port, opt_mysql_unix_port,
+ 0)))
+ {
+ fprintf(stderr,"%s: %s\n",my_progname,mysql_error(&mysql));
+ exit(1);
+ }
+ DBUG_PRINT("info", ("connected."));
+
+ for (x= 0; x < number_of_iterations; x++)
+ {
+ if (delimiter)
+ {
+ char *retstr;
+ char buf[HUGE_STRING_LENGTH];
+
+ while((retstr= strstr(script, delimiter)))
+ {
+ strncpy((char*)&buf, script, retstr - script);
+ buf[retstr - script]= '\0';
+ script+= retstr - script + delimiter_length;
+ DBUG_PRINT("info", ("running QUERY %s", (char *)buf));
+ if (mysql_query(&mysql, (char *)buf))
+ {
+ fprintf(stderr,"%s: Cannot run query %s ERROR : %s\n",
+ my_progname, buf, mysql_error(&mysql));
+ exit(1);
+ }
+
+ result= mysql_store_result(&mysql);
+ while ((row = mysql_fetch_row(result)))
+ {
+ counter++;
+ }
+ mysql_free_result(result);
+ result= 0;
+ }
+ }
+ if ((strlen(script)) < 3)
+ DBUG_RETURN(0);
+ if (mysql_query(&mysql, script))
+ {
+ fprintf(stderr,"%s: Cannot run query %s ERROR : %s\n",
+ my_progname, script, mysql_error(&mysql));
+ exit(1);
+ }
+
+ result= mysql_store_result(&mysql);
+ while ((row = mysql_fetch_row(result)))
+ counter++;
+ mysql_free_result(result);
+ result= 0;
+
+ }
+
+ mysql_close(&mysql);
+
+ DBUG_RETURN(0);
+}
+
+
+int
+load_data(const char *script)
+{
+ unsigned int x;
+ MYSQL mysql;
+
+ DBUG_ENTER("load_data");
+ DBUG_PRINT("info", ("task load_data, pid %d", getpid()));
+ mysql_init(&mysql);
+
+ if (!(mysql_real_connect(&mysql, host, user, opt_password,
+ "mysqlslap", opt_mysql_port, opt_mysql_unix_port,
+ 0)))
+ {
+ fprintf(stderr,"%s: Unable to connect to mysqlslap ERROR: %s\n",
+ my_progname, mysql_error(&mysql));
+ exit(1);
+ }
+
+ for (x= 0; x < number_of_rows; x++)
+ {
+ if (delimiter)
+ {
+ char *retstr;
+ char buf[HUGE_STRING_LENGTH];
+
+ while((retstr= strstr(script, delimiter)))
+ {
+ strncpy((char*)&buf, script, retstr - script);
+ buf[retstr - script]= '\0';
+ script+= retstr - script + delimiter_length;
+ DBUG_PRINT("info", ("running INSERT %s", (char *)buf));
+ if (mysql_query(&mysql, (char *)buf))
+ {
+ fprintf(stderr,"%s: Cannot run query %s ERROR : %s\n",
+ my_progname, buf, mysql_error(&mysql));
+ exit(1);
+ }
+ }
+ }
+ if ((strlen(script)) < 3)
+ DBUG_RETURN(0);
+ if (mysql_query(&mysql, script))
+ {
+ DBUG_PRINT("info", ("iteration %d with INSERT statement %s", script));
+ fprintf(stderr,"%s: Cannot insert into table using script: %s ERROR: %s\n",
+ my_progname, script, mysql_error(&mysql));
+ exit(1);
+ }
+ /* this causes variable data on the insert string */
+ if (auto_generate_sql)
+ {
+ build_insert_string();
+ strmov((char *)script, (char *)user_supplied_data);
+ }
+ }
+
+ mysql_close(&mysql);
+
+ DBUG_RETURN(0);
+}
+