Drizzled Public API Documentation

drizzleslap.cc
00001 /* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2010 Vijay Samuel
00005  *  Copyright (C) 2008 MySQL
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020  */
00021 
00022 
00023 /*
00024   Drizzle Slap
00025 
00026   A simple program designed to work as if multiple clients querying the database,
00027   then reporting the timing of each stage.
00028 
00029   Drizzle slap runs three stages:
00030   1) Create schema,table, and optionally any SP or data you want to beign
00031   the test with. (single client)
00032   2) Load test (many clients)
00033   3) Cleanup (disconnection, drop table if specified, single client)
00034 
00035   Examples:
00036 
00037   Supply your own create and query SQL statements, with 50 clients
00038   querying (200 selects for each):
00039 
00040   drizzleslap --delimiter=";" \
00041   --create="CREATE TABLE A (a int);INSERT INTO A VALUES (23)" \
00042   --query="SELECT * FROM A" --concurrency=50 --iterations=200
00043 
00044   Let the program build the query SQL statement with a table of two int
00045   columns, three varchar columns, five clients querying (20 times each),
00046   don't create the table or insert the data (using the previous test's
00047   schema and data):
00048 
00049   drizzleslap --concurrency=5 --iterations=20 \
00050   --number-int-cols=2 --number-char-cols=3 \
00051   --auto-generate-sql
00052 
00053   Tell the program to load the create, insert and query SQL statements from
00054   the specified files, where the create.sql file has multiple table creation
00055   statements delimited by ';' and multiple insert statements delimited by ';'.
00056   The --query file will have multiple queries delimited by ';', run all the
00057   load statements, and then run all the queries in the query file
00058   with five clients (five times each):
00059 
00060   drizzleslap --concurrency=5 \
00061   --iterations=5 --query=query.sql --create=create.sql \
00062   --delimiter=";"
00063 
00064   @todo
00065   Add language for better tests
00066   String length for files and those put on the command line are not
00067   setup to handle binary data.
00068   More stats
00069   Break up tests and run them on multiple hosts at once.
00070   Allow output to be fed into a database directly.
00071 
00072 */
00073 
00074 #include <config.h>
00075 #include "client/client_priv.h"
00076 
00077 #include "client/option_string.h"
00078 #include "client/stats.h"
00079 #include "client/thread_context.h"
00080 #include "client/conclusions.h"
00081 #include "client/wakeup.h"
00082 
00083 #include <signal.h>
00084 #include <stdarg.h>
00085 #include <sys/types.h>
00086 #include <sys/wait.h>
00087 #ifdef HAVE_SYS_STAT_H
00088 # include <sys/stat.h>
00089 #endif
00090 #include <fcntl.h>
00091 #include <math.h>
00092 #include <cassert>
00093 #include <cstdlib>
00094 #include <string>
00095 #include <iostream>
00096 #include <fstream>
00097 #include <drizzled/configmake.h>
00098 #include <memory>
00099 
00100 /* Added this for string translation. */
00101 #include <drizzled/gettext.h>
00102 
00103 #include <boost/algorithm/string.hpp>
00104 #include <boost/thread.hpp>
00105 #include <boost/thread/mutex.hpp>
00106 #include <boost/thread/condition_variable.hpp>
00107 #include <boost/program_options.hpp>
00108 #include <boost/scoped_ptr.hpp>
00109 #include <drizzled/atomics.h>
00110 
00111 #define SLAP_NAME "drizzleslap"
00112 #define SLAP_VERSION "1.5"
00113 
00114 #define HUGE_STRING_LENGTH 8196
00115 #define RAND_STRING_SIZE 126
00116 #define DEFAULT_BLOB_SIZE 1024
00117 
00118 using namespace std;
00119 using namespace drizzled;
00120 namespace po= boost::program_options;
00121 
00122 #ifdef HAVE_SMEM
00123 static char *shared_memory_base_name=0;
00124 #endif
00125 
00126 client::Wakeup master_wakeup;
00127 
00128 /* Global Thread timer */
00129 static bool timer_alarm= false;
00130 boost::mutex timer_alarm_mutex;
00131 boost::condition_variable_any timer_alarm_threshold;
00132 
00133 std::vector < std::string > primary_keys;
00134 
00135 drizzled::atomic<size_t> connection_count;
00136 drizzled::atomic<uint64_t> failed_update_for_transaction;
00137 
00138 static string host, 
00139   opt_password, 
00140   user,
00141   user_supplied_query,
00142   user_supplied_pre_statements,
00143   user_supplied_post_statements,
00144   default_engine,
00145   pre_system,
00146   post_system;
00147 
00148 static vector<string> user_supplied_queries;
00149 static string opt_verbose;
00150 std::string opt_protocol;
00151 string delimiter;
00152 
00153 string create_schema_string;
00154 
00155 static bool use_drizzle_protocol= false;
00156 static bool opt_preserve= true;
00157 static bool opt_only_print;
00158 static bool opt_burnin;
00159 static bool opt_ignore_sql_errors= false;
00160 static bool opt_silent,
00161   auto_generate_sql_autoincrement,
00162   auto_generate_sql_guid_primary,
00163   auto_generate_sql;
00164 std::string opt_auto_generate_sql_type;
00165 
00166 static int32_t verbose= 0;
00167 static uint32_t delimiter_length;
00168 static uint32_t commit_rate;
00169 static uint32_t detach_rate;
00170 static uint32_t opt_timer_length;
00171 static uint32_t opt_delayed_start;
00172 string num_blob_cols_opt,
00173   num_char_cols_opt,
00174   num_int_cols_opt;
00175 string opt_label;
00176 static uint32_t opt_set_random_seed;
00177 
00178 string auto_generate_selected_columns_opt;
00179 
00180 /* Yes, we do set defaults here */
00181 static uint32_t num_int_cols= 1;
00182 static uint32_t num_char_cols= 1;
00183 static uint32_t num_blob_cols= 0;
00184 static uint32_t num_blob_cols_size;
00185 static uint32_t num_blob_cols_size_min;
00186 static uint32_t num_int_cols_index= 0;
00187 static uint32_t num_char_cols_index= 0;
00188 static uint32_t iterations;
00189 static uint64_t actual_queries= 0;
00190 static uint64_t auto_actual_queries;
00191 static uint64_t auto_generate_sql_unique_write_number;
00192 static uint64_t auto_generate_sql_unique_query_number;
00193 static uint32_t auto_generate_sql_secondary_indexes;
00194 static uint64_t num_of_query;
00195 static uint64_t auto_generate_sql_number;
00196 string concurrency_str;
00197 string create_string;
00198 std::vector <uint32_t> concurrency;
00199 
00200 std::string opt_csv_str;
00201 int csv_file;
00202 
00203 static int process_options(void);
00204 static uint32_t opt_drizzle_port= 0;
00205 
00206 static OptionString *engine_options= NULL;
00207 static OptionString *query_options= NULL;
00208 static Statement *pre_statements= NULL;
00209 static Statement *post_statements= NULL;
00210 static Statement *create_statements= NULL;
00211 
00212 static std::vector <Statement *> query_statements;
00213 static uint32_t query_statements_count;
00214 
00215 
00216 /* Prototypes */
00217 void print_conclusions(Conclusions &con);
00218 void print_conclusions_csv(Conclusions &con);
00219 void generate_stats(Conclusions *con, OptionString *eng, Stats *sptr);
00220 uint32_t parse_comma(const char *string, std::vector <uint32_t> &range);
00221 uint32_t parse_delimiter(const char *script, Statement **stmt, char delm);
00222 uint32_t parse_option(const char *origin, OptionString **stmt, char delm);
00223 static void drop_schema(drizzle_con_st &con, const char *db);
00224 uint32_t get_random_string(char *buf, size_t size);
00225 static Statement *build_table_string(void);
00226 static Statement *build_insert_string(void);
00227 static Statement *build_update_string(void);
00228 static Statement * build_select_string(bool key);
00229 static int generate_primary_key_list(drizzle_con_st &con, OptionString *engine_stmt);
00230 static void create_schema(drizzle_con_st &con, const char *db, Statement *stmt, OptionString *engine_stmt, Stats *sptr);
00231 static void run_scheduler(Stats *sptr, Statement **stmts, uint32_t concur, uint64_t limit);
00232 void statement_cleanup(Statement *stmt);
00233 void option_cleanup(OptionString *stmt);
00234 void concurrency_loop(drizzle_con_st &con, uint32_t current, OptionString *eptr);
00235 static void run_statements(drizzle_con_st &con, Statement *stmt);
00236 drizzle_con_st *slap_connect(bool connect_to_schema);
00237 void slap_close(drizzle_con_st *con);
00238 static int run_query(drizzle_con_st &con, drizzle_result_st *result, const char *query, int len);
00239 void standard_deviation(Conclusions &con, Stats *sptr);
00240 
00241 static const char ALPHANUMERICS[]=
00242 "0123456789ABCDEFGHIJKLMNOPQRSTWXYZabcdefghijklmnopqrstuvwxyz";
00243 
00244 #define ALPHANUMERICS_SIZE (sizeof(ALPHANUMERICS)-1)
00245 
00246 
00247 static long int timedif(struct timeval a, struct timeval b)
00248 {
00249   int us, s;
00250 
00251   us = a.tv_usec - b.tv_usec;
00252   us /= 1000;
00253   s = a.tv_sec - b.tv_sec;
00254   s *= 1000;
00255   return s + us;
00256 }
00257 
00258 static void combine_queries(vector<string> queries)
00259 {
00260   user_supplied_query.erase();
00261   for (vector<string>::iterator it= queries.begin();
00262        it != queries.end();
00263        ++it)
00264   {
00265     user_supplied_query.append(*it);
00266     user_supplied_query.append(delimiter);
00267   }
00268 }
00269 
00270 
00271 static void run_task(ThreadContext *ctx)
00272 {
00273   uint64_t counter= 0, queries;
00274   uint64_t detach_counter;
00275   uint32_t commit_counter;
00276   drizzle_result_st result;
00277   drizzle_row_t row;
00278   Statement *ptr;
00279 
00280   master_wakeup.wait();
00281 
00282   drizzle_con_st *con= slap_connect(true);
00283 
00284   if (verbose >= 3)
00285   {
00286     printf("connected!\n");
00287   }
00288   queries= 0;
00289 
00290   commit_counter= 0;
00291   if (commit_rate)
00292   {
00293     run_query(*con, NULL, "SET AUTOCOMMIT=0", strlen("SET AUTOCOMMIT=0"));
00294   }
00295 
00296 limit_not_met:
00297   for (ptr= ctx->getStmt(), detach_counter= 0;
00298        ptr && ptr->getLength();
00299        ptr= ptr->getNext(), detach_counter++)
00300   {
00301     if (not opt_only_print && detach_rate && !(detach_counter % detach_rate))
00302     {
00303       slap_close(con);
00304       con= slap_connect(true);
00305     }
00306 
00307     /*
00308       We have to execute differently based on query type. This should become a function.
00309     */
00310     bool is_failed_update= false;
00311     if ((ptr->getType() == UPDATE_TYPE_REQUIRES_PREFIX) ||
00312         (ptr->getType() == SELECT_TYPE_REQUIRES_PREFIX))
00313     {
00314       uint32_t key_val;
00315       char buffer[HUGE_STRING_LENGTH];
00316 
00317       /*
00318         This should only happen if some sort of new engine was
00319         implemented that didn't properly handle UPDATEs.
00320 
00321         Just in case someone runs this under an experimental engine we don't
00322         want a crash so the if() is placed here.
00323       */
00324       assert(primary_keys.size());
00325       if (primary_keys.size())
00326       {
00327         key_val= (uint32_t)(random() % primary_keys.size());
00328         const char *key= primary_keys[key_val].c_str();
00329 
00330         assert(key);
00331 
00332         int length= snprintf(buffer, HUGE_STRING_LENGTH, "%.*s '%s'", (int)ptr->getLength(), ptr->getString(), key);
00333 
00334         if (run_query(*con, &result, buffer, length))
00335         {
00336           if ((ptr->getType() == UPDATE_TYPE_REQUIRES_PREFIX) and commit_rate)
00337           {
00338             // Expand to check to see if Innodb, if so we should restart the
00339             // transaction.  
00340 
00341             is_failed_update= true;
00342             failed_update_for_transaction.fetch_and_increment();
00343           }
00344           else
00345           {
00346             fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n",
00347                     SLAP_NAME, (uint32_t)length, buffer, drizzle_con_error(con));
00348             abort();
00349           }
00350         }
00351       }
00352     }
00353     else
00354     {
00355       if (run_query(*con, &result, ptr->getString(), ptr->getLength()))
00356       {
00357         if ((ptr->getType() == UPDATE_TYPE_REQUIRES_PREFIX) and commit_rate)
00358         {
00359           // Expand to check to see if Innodb, if so we should restart the
00360           // transaction.
00361 
00362           is_failed_update= true;
00363           failed_update_for_transaction.fetch_and_increment();
00364         }
00365         else
00366         {
00367           fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n",
00368                   SLAP_NAME, (uint32_t)ptr->getLength(), ptr->getString(), drizzle_con_error(con));
00369           abort();
00370         }
00371       }
00372     }
00373 
00374     if (not opt_only_print and not is_failed_update)
00375     {
00376       while ((row = drizzle_row_next(&result)))
00377         counter++;
00378       drizzle_result_free(&result);
00379     }
00380     queries++;
00381 
00382     if (commit_rate && (++commit_counter == commit_rate) and not is_failed_update)
00383     {
00384       commit_counter= 0;
00385       run_query(*con, NULL, "COMMIT", strlen("COMMIT"));
00386     }
00387 
00388     /* If the timer is set, and the alarm is not active then end */
00389     if (opt_timer_length && timer_alarm == false)
00390       goto end;
00391 
00392     /* If limit has been reached, and we are not in a timer_alarm just end */
00393     if (ctx->getLimit() && queries == ctx->getLimit() && timer_alarm == false)
00394       goto end;
00395   }
00396 
00397   if (opt_timer_length && timer_alarm == true)
00398     goto limit_not_met;
00399 
00400   if (ctx->getLimit() && queries < ctx->getLimit())
00401     goto limit_not_met;
00402 
00403 
00404 end:
00405   if (commit_rate)
00406   {
00407     run_query(*con, NULL, "COMMIT", strlen("COMMIT"));
00408   }
00409 
00410   slap_close(con);
00411   con= NULL;
00412 
00413   delete ctx;
00414 }
00415 
00434 int main(int argc, char **argv)
00435 {
00436   char *password= NULL;
00437   try
00438   {
00439     po::options_description commandline_options("Options used only in command line");
00440     commandline_options.add_options()
00441       ("help,?","Display this help and exit")
00442       ("info","Gives information and exit")
00443       ("burnin",po::value<bool>(&opt_burnin)->default_value(false)->zero_tokens(),
00444        "Run full test case in infinite loop")
00445       ("ignore-sql-errors", po::value<bool>(&opt_ignore_sql_errors)->default_value(false)->zero_tokens(),
00446        "Ignore SQL errors in query run")
00447       ("create-schema",po::value<string>(&create_schema_string)->default_value("drizzleslap"),
00448        "Schema to run tests in")
00449       ("create",po::value<string>(&create_string)->default_value(""),
00450        "File or string to use to create tables")
00451       ("detach",po::value<uint32_t>(&detach_rate)->default_value(0),
00452        "Detach (close and re open) connections after X number of requests")
00453       ("iterations,i",po::value<uint32_t>(&iterations)->default_value(1),
00454        "Number of times to run the tests")
00455       ("label",po::value<string>(&opt_label)->default_value(""),
00456        "Label to use for print and csv")
00457       ("number-blob-cols",po::value<string>(&num_blob_cols_opt)->default_value(""),
00458        "Number of BLOB columns to create table with if specifying --auto-generate-sql. Example --number-blob-cols=3:1024/2048 would give you 3 blobs with a random size between 1024 and 2048. ")
00459       ("number-char-cols,x",po::value<string>(&num_char_cols_opt)->default_value(""),
00460        "Number of VARCHAR columns to create in table if specifying --auto-generate-sql.")
00461       ("number-int-cols,y",po::value<string>(&num_int_cols_opt)->default_value(""),
00462        "Number of INT columns to create in table if specifying --auto-generate-sql.")
00463       ("number-of-queries",
00464        po::value<uint64_t>(&num_of_query)->default_value(0),
00465        "Limit each client to this number of queries(this is not exact)") 
00466       ("only-print",po::value<bool>(&opt_only_print)->default_value(false)->zero_tokens(),
00467        "This causes drizzleslap to not connect to the database instead print out what it would have done instead")
00468       ("post-query", po::value<string>(&user_supplied_post_statements)->default_value(""),
00469        "Query to run or file containing query to execute after tests have completed.")
00470       ("post-system",po::value<string>(&post_system)->default_value(""),
00471        "system() string to execute after tests have completed")
00472       ("pre-query",
00473        po::value<string>(&user_supplied_pre_statements)->default_value(""),
00474        "Query to run or file containing query to execute before running tests.")
00475       ("pre-system",po::value<string>(&pre_system)->default_value(""),
00476        "system() string to execute before running tests.")
00477       ("query,q",po::value<vector<string> >(&user_supplied_queries)->composing()->notifier(&combine_queries),
00478        "Query to run or file containing query")
00479       ("verbose,v", po::value<string>(&opt_verbose)->default_value("v"), "Increase verbosity level by one.")
00480       ("version,V","Output version information and exit") 
00481       ;
00482 
00483     po::options_description slap_options("Options specific to drizzleslap");
00484     slap_options.add_options()
00485       ("auto-generate-sql-select-columns",
00486        po::value<string>(&auto_generate_selected_columns_opt)->default_value(""),
00487        "Provide a string to use for the select fields used in auto tests")
00488       ("auto-generate-sql,a",po::value<bool>(&auto_generate_sql)->default_value(false)->zero_tokens(),
00489        "Generate SQL where not supplied by file or command line")  
00490       ("auto-generate-sql-add-autoincrement",
00491        po::value<bool>(&auto_generate_sql_autoincrement)->default_value(false)->zero_tokens(),
00492        "Add an AUTO_INCREMENT column to auto-generated tables")
00493       ("auto-generate-sql-execute-number",
00494        po::value<uint64_t>(&auto_actual_queries)->default_value(0),
00495        "See this number and generate a set of queries to run")
00496       ("auto-generate-sql-guid-primary",
00497        po::value<bool>(&auto_generate_sql_guid_primary)->default_value(false)->zero_tokens(),
00498        "Add GUID based primary keys to auto-generated tables")
00499       ("auto-generate-sql-load-type",
00500        po::value<string>(&opt_auto_generate_sql_type)->default_value("mixed"),
00501        "Specify test load type: mixed, update, write, key or read; default is mixed")  
00502       ("auto-generate-sql-secondary-indexes",
00503        po::value<uint32_t>(&auto_generate_sql_secondary_indexes)->default_value(0),
00504        "Number of secondary indexes to add to auto-generated tables")
00505       ("auto-generated-sql-unique-query-number",
00506        po::value<uint64_t>(&auto_generate_sql_unique_query_number)->default_value(10),
00507        "Number of unique queries to generate for automatic tests")
00508       ("auto-generate-sql-unique-write-number",
00509        po::value<uint64_t>(&auto_generate_sql_unique_write_number)->default_value(10),
00510        "Number of unique queries to generate for auto-generate-sql-write-number")
00511       ("auto-generate-sql-write-number",
00512        po::value<uint64_t>(&auto_generate_sql_number)->default_value(100),
00513        "Number of row inserts to perform for each thread (default is 100).")
00514       ("commit",po::value<uint32_t>(&commit_rate)->default_value(0),
00515        "Commit records every X number of statements")
00516       ("concurrency,c",po::value<string>(&concurrency_str)->default_value(""),
00517        "Number of clients to simulate for query to run")
00518       ("csv",po::value<std::string>(&opt_csv_str)->default_value(""),
00519        "Generate CSV output to named file or to stdout if no file is name.")
00520       ("delayed-start",po::value<uint32_t>(&opt_delayed_start)->default_value(0),
00521        "Delay the startup of threads by a random number of microsends (the maximum of the delay")
00522       ("delimiter,F",po::value<string>(&delimiter)->default_value("\n"),
00523        "Delimiter to use in SQL statements supplied in file or command line")
00524       ("engine,e",po::value<string>(&default_engine)->default_value(""),
00525        "Storage engine to use for creating the table")
00526       ("set-random-seed",
00527        po::value<uint32_t>(&opt_set_random_seed)->default_value(0), 
00528        "Seed for random number generator (srandom(3)) ") 
00529       ("silent,s",po::value<bool>(&opt_silent)->default_value(false)->zero_tokens(),
00530        "Run program in silent mode - no output. ") 
00531       ("timer-length",po::value<uint32_t>(&opt_timer_length)->default_value(0),
00532        "Require drizzleslap to run each specific test a certain amount of time in seconds")  
00533       ;
00534 
00535     po::options_description client_options("Options specific to the client");
00536     client_options.add_options()
00537       ("host,h",po::value<string>(&host)->default_value("localhost"),"Connect to the host")
00538       ("password,P",po::value<char *>(&password),
00539        "Password to use when connecting to server. If password is not given it's asked from the tty")
00540       ("port,p",po::value<uint32_t>(), "Port number to use for connection")
00541       ("protocol",po::value<string>(&opt_protocol)->default_value("mysql"),
00542        "The protocol of connection (mysql or drizzle).")
00543       ("user,u",po::value<string>(&user)->default_value(""),
00544        "User for login if not current user")  
00545       ;
00546 
00547     po::options_description long_options("Allowed Options");
00548     long_options.add(commandline_options).add(slap_options).add(client_options);
00549 
00550     std::string system_config_dir_slap(SYSCONFDIR); 
00551     system_config_dir_slap.append("/drizzle/drizzleslap.cnf");
00552 
00553     std::string system_config_dir_client(SYSCONFDIR); 
00554     system_config_dir_client.append("/drizzle/client.cnf");
00555 
00556     std::string user_config_dir((getenv("XDG_CONFIG_HOME")? getenv("XDG_CONFIG_HOME"):"~/.config"));
00557 
00558     if (user_config_dir.compare(0, 2, "~/") == 0)
00559     {
00560       char *homedir;
00561       homedir= getenv("HOME");
00562       if (homedir != NULL)
00563         user_config_dir.replace(0, 1, homedir);
00564     }
00565 
00566     uint64_t temp_drizzle_port= 0;
00567     boost::scoped_ptr<drizzle_con_st> con_ap(new drizzle_con_st);
00568     OptionString *eptr;
00569 
00570     // Disable allow_guessing
00571     int style = po::command_line_style::default_style & ~po::command_line_style::allow_guessing;
00572 
00573     po::variables_map vm;
00574     po::store(po::command_line_parser(argc, argv).options(long_options).
00575               style(style).extra_parser(parse_password_arg).run(), vm);
00576 
00577     std::string user_config_dir_slap(user_config_dir);
00578     user_config_dir_slap.append("/drizzle/drizzleslap.cnf"); 
00579 
00580     std::string user_config_dir_client(user_config_dir);
00581     user_config_dir_client.append("/drizzle/client.cnf");
00582 
00583     ifstream user_slap_ifs(user_config_dir_slap.c_str());
00584     po::store(parse_config_file(user_slap_ifs, slap_options), vm);
00585 
00586     ifstream user_client_ifs(user_config_dir_client.c_str());
00587     po::store(parse_config_file(user_client_ifs, client_options), vm);
00588 
00589     ifstream system_slap_ifs(system_config_dir_slap.c_str());
00590     store(parse_config_file(system_slap_ifs, slap_options), vm);
00591 
00592     ifstream system_client_ifs(system_config_dir_client.c_str());
00593     store(parse_config_file(system_client_ifs, client_options), vm);
00594 
00595     po::notify(vm);
00596 
00597     if (process_options())
00598       abort();
00599 
00600     if ( vm.count("help") || vm.count("info"))
00601     {
00602       printf("%s  Ver %s Distrib %s, for %s-%s (%s)\n",SLAP_NAME, SLAP_VERSION,
00603           drizzle_version(),HOST_VENDOR,HOST_OS,HOST_CPU);
00604       puts("Copyright (C) 2008 Sun Microsystems");
00605       puts("This software comes with ABSOLUTELY NO WARRANTY. "
00606            "This is free software,\n"
00607            "and you are welcome to modify and redistribute it under the GPL "
00608            "license\n");
00609       puts("Run a query multiple times against the server\n");
00610       cout << long_options << endl;
00611       abort();
00612     }   
00613 
00614     if (vm.count("protocol"))
00615     {
00616       boost::to_lower(opt_protocol);
00617       if (not opt_protocol.compare("mysql"))
00618         use_drizzle_protocol=false;
00619       else if (not opt_protocol.compare("drizzle"))
00620         use_drizzle_protocol=true;
00621       else
00622       {
00623         cout << _("Error: Unknown protocol") << " '" << opt_protocol << "'" << endl;
00624         abort();
00625       }
00626     }
00627     if (vm.count("port")) 
00628     {
00629       temp_drizzle_port= vm["port"].as<uint32_t>();
00630 
00631       if ((temp_drizzle_port == 0) || (temp_drizzle_port > 65535))
00632       {
00633         fprintf(stderr, _("Value supplied for port is not valid.\n"));
00634         abort();
00635       }
00636       else
00637       {
00638         opt_drizzle_port= (uint32_t) temp_drizzle_port;
00639       }
00640     }
00641 
00642   if ( vm.count("password") )
00643   {
00644     if (not opt_password.empty())
00645       opt_password.erase();
00646     if (password == PASSWORD_SENTINEL)
00647     {
00648       opt_password= "";
00649     }
00650     else
00651     {
00652       opt_password= password;
00653       tty_password= false;
00654     }
00655   }
00656   else
00657   {
00658       tty_password= true;
00659   }
00660 
00661 
00662 
00663     if ( vm.count("version") )
00664     {
00665       printf("%s  Ver %s Distrib %s, for %s-%s (%s)\n",SLAP_NAME, SLAP_VERSION,
00666           drizzle_version(),HOST_VENDOR,HOST_OS,HOST_CPU);
00667       abort();
00668     }
00669 
00670     /* Seed the random number generator if we will be using it. */
00671     if (auto_generate_sql)
00672     {
00673       if (opt_set_random_seed == 0)
00674         opt_set_random_seed= (uint32_t)time(NULL);
00675       srandom(opt_set_random_seed);
00676     }
00677 
00678     /* globals? Yes, so we only have to run strlen once */
00679     delimiter_length= delimiter.length();
00680 
00681     drizzle_con_st *con= slap_connect(false);
00682 
00683     /* Main iterations loop */
00684 burnin:
00685     eptr= engine_options;
00686     do
00687     {
00688       /* For the final stage we run whatever queries we were asked to run */
00689       uint32_t *current;
00690 
00691       if (verbose >= 2)
00692         printf("Starting Concurrency Test\n");
00693 
00694       if (concurrency.size())
00695       {
00696         for (current= &concurrency[0]; current && *current; current++)
00697           concurrency_loop(*con, *current, eptr);
00698       }
00699       else
00700       {
00701         uint32_t infinite= 1;
00702         do {
00703           concurrency_loop(*con, infinite, eptr);
00704         }
00705         while (infinite++);
00706       }
00707 
00708       if (not opt_preserve)
00709         drop_schema(*con, create_schema_string.c_str());
00710 
00711     } while (eptr ? (eptr= eptr->getNext()) : 0);
00712 
00713     if (opt_burnin)
00714     {
00715       goto burnin;
00716     }
00717 
00718     slap_close(con);
00719 
00720     /* now free all the strings we created */
00721     if (not opt_password.empty())
00722       opt_password.erase();
00723 
00724     concurrency.clear();
00725 
00726     statement_cleanup(create_statements);
00727     for (uint32_t x= 0; x < query_statements_count; x++)
00728       statement_cleanup(query_statements[x]);
00729     query_statements.clear();
00730     statement_cleanup(pre_statements);
00731     statement_cleanup(post_statements);
00732     option_cleanup(engine_options);
00733     option_cleanup(query_options);
00734 
00735 #ifdef HAVE_SMEM
00736     free(shared_memory_base_name);
00737 #endif
00738 
00739   }
00740 
00741   catch(std::exception &err)
00742   {
00743     cerr<<"Error:"<<err.what()<<endl;
00744   }
00745 
00746   if (csv_file != fileno(stdout))
00747     close(csv_file);
00748 
00749   return 0;
00750 }
00751 
00752 void concurrency_loop(drizzle_con_st &con, uint32_t current, OptionString *eptr)
00753 {
00754   Stats *head_sptr;
00755   Stats *sptr;
00756   Conclusions conclusion;
00757   uint64_t client_limit;
00758 
00759   head_sptr= new Stats[iterations];
00760   if (head_sptr == NULL)
00761   {
00762     fprintf(stderr,"Error allocating memory in concurrency_loop\n");
00763     abort();
00764   }
00765 
00766   if (auto_actual_queries)
00767     client_limit= auto_actual_queries;
00768   else if (num_of_query)
00769     client_limit=  num_of_query / current;
00770   else
00771     client_limit= actual_queries;
00772 
00773   uint32_t x;
00774   for (x= 0, sptr= head_sptr; x < iterations; x++, sptr++)
00775   {
00776     /*
00777       We might not want to load any data, such as when we are calling
00778       a stored_procedure that doesn't use data, or we know we already have
00779       data in the table.
00780     */
00781     if (opt_preserve == false)
00782       drop_schema(con, create_schema_string.c_str());
00783 
00784     /* First we create */
00785     if (create_statements)
00786       create_schema(con, create_schema_string.c_str(), create_statements, eptr, sptr);
00787 
00788     /*
00789       If we generated GUID we need to build a list of them from creation that
00790       we can later use.
00791     */
00792     if (verbose >= 2)
00793       printf("Generating primary key list\n");
00794     if (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary)
00795       generate_primary_key_list(con, eptr);
00796 
00797     if (not pre_system.empty())
00798     {
00799       int ret= system(pre_system.c_str());
00800       assert(ret != -1);
00801     }
00802 
00803     /*
00804       Pre statements are always run after all other logic so they can
00805       correct/adjust any item that they want.
00806     */
00807     if (pre_statements)
00808       run_statements(con, pre_statements);
00809 
00810     run_scheduler(sptr, &query_statements[0], current, client_limit);
00811 
00812     if (post_statements)
00813       run_statements(con, post_statements);
00814 
00815     if (not post_system.empty())
00816     {
00817       int ret=  system(post_system.c_str());
00818       assert(ret !=-1);
00819     }
00820 
00821     /* We are finished with this run */
00822     if (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary)
00823       primary_keys.clear();
00824   }
00825 
00826   if (verbose >= 2)
00827     printf("Generating stats\n");
00828 
00829   generate_stats(&conclusion, eptr, head_sptr);
00830 
00831   if (not opt_silent)
00832     print_conclusions(conclusion);
00833   if (not opt_csv_str.empty())
00834     print_conclusions_csv(conclusion);
00835 
00836   delete[] head_sptr;
00837 }
00838 
00839 
00840 uint32_t get_random_string(char *buf, size_t size)
00841 {
00842   char *buf_ptr= buf;
00843 
00844   for (size_t x= size; x > 0; x--)
00845     *buf_ptr++= ALPHANUMERICS[random() % ALPHANUMERICS_SIZE];
00846   return(buf_ptr - buf);
00847 }
00848 
00849 
00850 /*
00851   build_table_string
00852 
00853   This function builds a create table query if the user opts to not supply
00854   a file or string containing a create table statement
00855 */
00856 static Statement *
00857 build_table_string(void)
00858 {
00859   char       buf[HUGE_STRING_LENGTH];
00860   uint32_t        col_count;
00861   Statement *ptr;
00862   string table_string;
00863 
00864   table_string.reserve(HUGE_STRING_LENGTH);
00865 
00866   table_string= "CREATE TABLE `t1` (";
00867 
00868   if (auto_generate_sql_autoincrement)
00869   {
00870     table_string.append("id serial");
00871 
00872     if (num_int_cols || num_char_cols)
00873       table_string.append(",");
00874   }
00875 
00876   if (auto_generate_sql_guid_primary)
00877   {
00878     table_string.append("id varchar(128) primary key");
00879 
00880     if (num_int_cols || num_char_cols || auto_generate_sql_guid_primary)
00881       table_string.append(",");
00882   }
00883 
00884   if (auto_generate_sql_secondary_indexes)
00885   {
00886     for (uint32_t count= 0; count < auto_generate_sql_secondary_indexes; count++)
00887     {
00888       if (count) /* Except for the first pass we add a comma */
00889         table_string.append(",");
00890 
00891       if (snprintf(buf, HUGE_STRING_LENGTH, "id%d varchar(32) unique key", count)
00892           > HUGE_STRING_LENGTH)
00893       {
00894         fprintf(stderr, "Memory Allocation error in create table\n");
00895         abort();
00896       }
00897       table_string.append(buf);
00898     }
00899 
00900     if (num_int_cols || num_char_cols)
00901       table_string.append(",");
00902   }
00903 
00904   if (num_int_cols)
00905     for (col_count= 1; col_count <= num_int_cols; col_count++)
00906     {
00907       if (num_int_cols_index)
00908       {
00909         if (snprintf(buf, HUGE_STRING_LENGTH, "intcol%d INT, INDEX(intcol%d)",
00910                      col_count, col_count) > HUGE_STRING_LENGTH)
00911         {
00912           fprintf(stderr, "Memory Allocation error in create table\n");
00913           abort();
00914         }
00915       }
00916       else
00917       {
00918         if (snprintf(buf, HUGE_STRING_LENGTH, "intcol%d INT ", col_count)
00919             > HUGE_STRING_LENGTH)
00920         {
00921           fprintf(stderr, "Memory Allocation error in create table\n");
00922           abort();
00923         }
00924       }
00925       table_string.append(buf);
00926 
00927       if (col_count < num_int_cols || num_char_cols > 0)
00928         table_string.append(",");
00929     }
00930 
00931   if (num_char_cols)
00932     for (col_count= 1; col_count <= num_char_cols; col_count++)
00933     {
00934       if (num_char_cols_index)
00935       {
00936         if (snprintf(buf, HUGE_STRING_LENGTH,
00937                      "charcol%d VARCHAR(128), INDEX(charcol%d) ",
00938                      col_count, col_count) > HUGE_STRING_LENGTH)
00939         {
00940           fprintf(stderr, "Memory Allocation error in creating table\n");
00941           abort();
00942         }
00943       }
00944       else
00945       {
00946         if (snprintf(buf, HUGE_STRING_LENGTH, "charcol%d VARCHAR(128)",
00947                      col_count) > HUGE_STRING_LENGTH)
00948         {
00949           fprintf(stderr, "Memory Allocation error in creating table\n");
00950           abort();
00951         }
00952       }
00953       table_string.append(buf);
00954 
00955       if (col_count < num_char_cols || num_blob_cols > 0)
00956         table_string.append(",");
00957     }
00958 
00959   if (num_blob_cols)
00960     for (col_count= 1; col_count <= num_blob_cols; col_count++)
00961     {
00962       if (snprintf(buf, HUGE_STRING_LENGTH, "blobcol%d blob",
00963                    col_count) > HUGE_STRING_LENGTH)
00964       {
00965         fprintf(stderr, "Memory Allocation error in creating table\n");
00966         abort();
00967       }
00968       table_string.append(buf);
00969 
00970       if (col_count < num_blob_cols)
00971         table_string.append(",");
00972     }
00973 
00974   table_string.append(")");
00975   ptr= new Statement;
00976   ptr->setString(table_string.length());
00977   if (ptr->getString()==NULL)
00978   {
00979     fprintf(stderr, "Memory Allocation error in creating table\n");
00980     abort();
00981   }
00982   ptr->setType(CREATE_TABLE_TYPE);
00983   strcpy(ptr->getString(), table_string.c_str());
00984   return(ptr);
00985 }
00986 
00987 /*
00988   build_update_string()
00989 
00990   This function builds insert statements when the user opts to not supply
00991   an insert file or string containing insert data
00992 */
00993 static Statement *
00994 build_update_string(void)
00995 {
00996   char       buf[HUGE_STRING_LENGTH];
00997   uint32_t        col_count;
00998   Statement *ptr;
00999   string update_string;
01000 
01001   update_string.reserve(HUGE_STRING_LENGTH);
01002 
01003   update_string= "UPDATE t1 SET ";
01004 
01005   if (num_int_cols)
01006     for (col_count= 1; col_count <= num_int_cols; col_count++)
01007     {
01008       if (snprintf(buf, HUGE_STRING_LENGTH, "intcol%d = %ld", col_count,
01009                    random()) > HUGE_STRING_LENGTH)
01010       {
01011         fprintf(stderr, "Memory Allocation error in creating update\n");
01012         abort();
01013       }
01014       update_string.append(buf);
01015 
01016       if (col_count < num_int_cols || num_char_cols > 0)
01017         update_string.append(",", 1);
01018     }
01019 
01020   if (num_char_cols)
01021     for (col_count= 1; col_count <= num_char_cols; col_count++)
01022     {
01023       char rand_buffer[RAND_STRING_SIZE];
01024       int buf_len= get_random_string(rand_buffer, RAND_STRING_SIZE);
01025 
01026       if (snprintf(buf, HUGE_STRING_LENGTH, "charcol%d = '%.*s'", col_count,
01027                    buf_len, rand_buffer)
01028           > HUGE_STRING_LENGTH)
01029       {
01030         fprintf(stderr, "Memory Allocation error in creating update\n");
01031         abort();
01032       }
01033       update_string.append(buf);
01034 
01035       if (col_count < num_char_cols)
01036         update_string.append(",", 1);
01037     }
01038 
01039   if (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary)
01040     update_string.append(" WHERE id = ");
01041 
01042 
01043   ptr= new Statement;
01044 
01045   ptr->setString(update_string.length());
01046   if (ptr->getString() == NULL)
01047   {
01048     fprintf(stderr, "Memory Allocation error in creating update\n");
01049     abort();
01050   }
01051   if (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary)
01052     ptr->setType(UPDATE_TYPE_REQUIRES_PREFIX);
01053   else
01054     ptr->setType(UPDATE_TYPE);
01055   strncpy(ptr->getString(), update_string.c_str(), ptr->getLength());
01056   return(ptr);
01057 }
01058 
01059 
01060 /*
01061   build_insert_string()
01062 
01063   This function builds insert statements when the user opts to not supply
01064   an insert file or string containing insert data
01065 */
01066 static Statement *
01067 build_insert_string(void)
01068 {
01069   char       buf[HUGE_STRING_LENGTH];
01070   uint32_t        col_count;
01071   Statement *ptr;
01072   string insert_string;
01073 
01074   insert_string.reserve(HUGE_STRING_LENGTH);
01075 
01076   insert_string= "INSERT INTO t1 VALUES (";
01077 
01078   if (auto_generate_sql_autoincrement)
01079   {
01080     insert_string.append("NULL");
01081 
01082     if (num_int_cols || num_char_cols)
01083       insert_string.append(",");
01084   }
01085 
01086   if (auto_generate_sql_guid_primary)
01087   {
01088     insert_string.append("uuid()");
01089 
01090     if (num_int_cols || num_char_cols)
01091       insert_string.append(",");
01092   }
01093 
01094   if (auto_generate_sql_secondary_indexes)
01095   {
01096     uint32_t count;
01097 
01098     for (count= 0; count < auto_generate_sql_secondary_indexes; count++)
01099     {
01100       if (count) /* Except for the first pass we add a comma */
01101         insert_string.append(",");
01102 
01103       insert_string.append("uuid()");
01104     }
01105 
01106     if (num_int_cols || num_char_cols)
01107       insert_string.append(",");
01108   }
01109 
01110   if (num_int_cols)
01111     for (col_count= 1; col_count <= num_int_cols; col_count++)
01112     {
01113       if (snprintf(buf, HUGE_STRING_LENGTH, "%ld", random()) > HUGE_STRING_LENGTH)
01114       {
01115         fprintf(stderr, "Memory Allocation error in creating insert\n");
01116         abort();
01117       }
01118       insert_string.append(buf);
01119 
01120       if (col_count < num_int_cols || num_char_cols > 0)
01121         insert_string.append(",");
01122     }
01123 
01124   if (num_char_cols)
01125     for (col_count= 1; col_count <= num_char_cols; col_count++)
01126     {
01127       int buf_len= get_random_string(buf, RAND_STRING_SIZE);
01128       insert_string.append("'", 1);
01129       insert_string.append(buf, buf_len);
01130       insert_string.append("'", 1);
01131 
01132       if (col_count < num_char_cols || num_blob_cols > 0)
01133         insert_string.append(",", 1);
01134     }
01135 
01136   if (num_blob_cols)
01137   {
01138     vector <char> blob_ptr;
01139 
01140     blob_ptr.resize(num_blob_cols_size);
01141 
01142     for (col_count= 1; col_count <= num_blob_cols; col_count++)
01143     {
01144       uint32_t buf_len;
01145       uint32_t size;
01146       uint32_t difference= num_blob_cols_size - num_blob_cols_size_min;
01147 
01148       size= difference ? (num_blob_cols_size_min + (random() % difference)) :
01149         num_blob_cols_size;
01150 
01151       buf_len= get_random_string(&blob_ptr[0], size);
01152 
01153       insert_string.append("'", 1);
01154       insert_string.append(&blob_ptr[0], buf_len);
01155       insert_string.append("'", 1);
01156 
01157       if (col_count < num_blob_cols)
01158         insert_string.append(",", 1);
01159     }
01160   }
01161 
01162   insert_string.append(")", 1);
01163 
01164   ptr= new Statement;
01165   ptr->setString(insert_string.length());
01166   if (ptr->getString()==NULL)
01167   {
01168     fprintf(stderr, "Memory Allocation error in creating select\n");
01169     abort();
01170   }
01171   ptr->setType(INSERT_TYPE);
01172   strcpy(ptr->getString(), insert_string.c_str());
01173   return(ptr);
01174 }
01175 
01176 
01177 /*
01178   build_select_string()
01179 
01180   This function builds a query if the user opts to not supply a query
01181   statement or file containing a query statement
01182 */
01183 static Statement *
01184 build_select_string(bool key)
01185 {
01186   char       buf[HUGE_STRING_LENGTH];
01187   uint32_t        col_count;
01188   Statement *ptr;
01189   string query_string;
01190 
01191   query_string.reserve(HUGE_STRING_LENGTH);
01192 
01193   query_string.append("SELECT ", 7);
01194   if (not auto_generate_selected_columns_opt.empty())
01195   {
01196     query_string.append(auto_generate_selected_columns_opt.c_str());
01197   }
01198   else
01199   {
01200     for (col_count= 1; col_count <= num_int_cols; col_count++)
01201     {
01202       if (snprintf(buf, HUGE_STRING_LENGTH, "intcol%d", col_count)
01203           > HUGE_STRING_LENGTH)
01204       {
01205         fprintf(stderr, "Memory Allocation error in creating select\n");
01206         abort();
01207       }
01208       query_string.append(buf);
01209 
01210       if (col_count < num_int_cols || num_char_cols > 0)
01211         query_string.append(",", 1);
01212 
01213     }
01214     for (col_count= 1; col_count <= num_char_cols; col_count++)
01215     {
01216       if (snprintf(buf, HUGE_STRING_LENGTH, "charcol%d", col_count)
01217           > HUGE_STRING_LENGTH)
01218       {
01219         fprintf(stderr, "Memory Allocation error in creating select\n");
01220         abort();
01221       }
01222       query_string.append(buf);
01223 
01224       if (col_count < num_char_cols || num_blob_cols > 0)
01225         query_string.append(",", 1);
01226 
01227     }
01228     for (col_count= 1; col_count <= num_blob_cols; col_count++)
01229     {
01230       if (snprintf(buf, HUGE_STRING_LENGTH, "blobcol%d", col_count)
01231           > HUGE_STRING_LENGTH)
01232       {
01233         fprintf(stderr, "Memory Allocation error in creating select\n");
01234         abort();
01235       }
01236       query_string.append(buf);
01237 
01238       if (col_count < num_blob_cols)
01239         query_string.append(",", 1);
01240     }
01241   }
01242   query_string.append(" FROM t1");
01243 
01244   if ((key) &&
01245       (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary))
01246     query_string.append(" WHERE id = ");
01247 
01248   ptr= new Statement;
01249   ptr->setString(query_string.length());
01250   if (ptr->getString() == NULL)
01251   {
01252     fprintf(stderr, "Memory Allocation error in creating select\n");
01253     abort();
01254   }
01255   if ((key) &&
01256       (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary))
01257     ptr->setType(SELECT_TYPE_REQUIRES_PREFIX);
01258   else
01259     ptr->setType(SELECT_TYPE);
01260   strcpy(ptr->getString(), query_string.c_str());
01261   return(ptr);
01262 }
01263 
01264 static int
01265 process_options(void)
01266 {
01267   struct stat sbuf;
01268   ssize_t bytes_read= 0;
01269   
01270   if (user.empty())
01271     user= "root";
01272 
01273   verbose= opt_verbose.length();
01274 
01275   /* If something is created we clean it up, otherwise we leave schemas alone */
01276   if ( (not create_string.empty()) || auto_generate_sql)
01277     opt_preserve= false;
01278 
01279   if (auto_generate_sql && (not create_string.empty() || !user_supplied_query.empty()))
01280   {
01281     fprintf(stderr,
01282             "%s: Can't use --auto-generate-sql when create and query strings are specified!\n",
01283             SLAP_NAME);
01284     abort();
01285   }
01286 
01287   if (auto_generate_sql && auto_generate_sql_guid_primary &&
01288       auto_generate_sql_autoincrement)
01289   {
01290     fprintf(stderr,
01291             "%s: Either auto-generate-sql-guid-primary or auto-generate-sql-add-autoincrement can be used!\n",
01292             SLAP_NAME);
01293     abort();
01294   }
01295 
01296   if (auto_generate_sql && num_of_query && auto_actual_queries)
01297   {
01298     fprintf(stderr,
01299             "%s: Either auto-generate-sql-execute-number or number-of-queries can be used!\n",
01300             SLAP_NAME);
01301     abort();
01302   }
01303 
01304   parse_comma(not concurrency_str.empty() ? concurrency_str.c_str() : "1", concurrency);
01305 
01306   if (not opt_csv_str.empty())
01307   {
01308     opt_silent= true;
01309 
01310     if (opt_csv_str[0] == '-')
01311     {
01312       csv_file= fileno(stdout);
01313     }
01314     else
01315     {
01316       if ((csv_file= open(opt_csv_str.c_str(), O_CREAT|O_WRONLY|O_APPEND, 
01317                           S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1)
01318       {
01319         fprintf(stderr,"%s: Could not open csv file: %sn\n",
01320                 SLAP_NAME, opt_csv_str.c_str());
01321         abort();
01322       }
01323     }
01324   }
01325 
01326   if (opt_only_print)
01327     opt_silent= true;
01328 
01329   if (not num_int_cols_opt.empty())
01330   {
01331     OptionString *str;
01332     parse_option(num_int_cols_opt.c_str(), &str, ',');
01333     num_int_cols= atoi(str->getString());
01334     if (str->getOption())
01335       num_int_cols_index= atoi(str->getOption());
01336     option_cleanup(str);
01337   }
01338 
01339   if (not num_char_cols_opt.empty())
01340   {
01341     OptionString *str;
01342     parse_option(num_char_cols_opt.c_str(), &str, ',');
01343     num_char_cols= atoi(str->getString());
01344     if (str->getOption())
01345       num_char_cols_index= atoi(str->getOption());
01346     else
01347       num_char_cols_index= 0;
01348     option_cleanup(str);
01349   }
01350 
01351   uint32_t sql_type_count= 0;
01352   if (not num_blob_cols_opt.empty())
01353   {
01354     OptionString *str;
01355     parse_option(num_blob_cols_opt.c_str(), &str, ',');
01356     num_blob_cols= atoi(str->getString());
01357     if (str->getOption())
01358     {
01359       char *sep_ptr;
01360 
01361       if ((sep_ptr= strchr(str->getOption(), '/')))
01362       {
01363         num_blob_cols_size_min= atoi(str->getOption());
01364         num_blob_cols_size= atoi(sep_ptr+1);
01365       }
01366       else
01367       {
01368         num_blob_cols_size_min= num_blob_cols_size= atoi(str->getOption());
01369       }
01370     }
01371     else
01372     {
01373       num_blob_cols_size= DEFAULT_BLOB_SIZE;
01374       num_blob_cols_size_min= DEFAULT_BLOB_SIZE;
01375     }
01376     option_cleanup(str);
01377   }
01378 
01379 
01380   if (auto_generate_sql)
01381   {
01382     uint64_t x= 0;
01383     Statement *ptr_statement;
01384 
01385     if (verbose >= 2)
01386       printf("Building Create Statements for Auto\n");
01387 
01388     create_statements= build_table_string();
01389     /*
01390       Pre-populate table
01391     */
01392     for (ptr_statement= create_statements, x= 0;
01393          x < auto_generate_sql_unique_write_number;
01394          x++, ptr_statement= ptr_statement->getNext())
01395     {
01396       ptr_statement->setNext(build_insert_string());
01397     }
01398 
01399     if (verbose >= 2)
01400       printf("Building Query Statements for Auto\n");
01401 
01402     if (opt_auto_generate_sql_type.empty())
01403       opt_auto_generate_sql_type= "mixed";
01404 
01405     query_statements_count=
01406       parse_option(opt_auto_generate_sql_type.c_str(), &query_options, ',');
01407 
01408     query_statements.resize(query_statements_count);
01409 
01410     OptionString* sql_type= query_options;
01411     do
01412     {
01413       if (sql_type->getString()[0] == 'r')
01414       {
01415         if (verbose >= 2)
01416           printf("Generating SELECT Statements for Auto\n");
01417 
01418         query_statements[sql_type_count]= build_select_string(false);
01419         for (ptr_statement= query_statements[sql_type_count], x= 0;
01420              x < auto_generate_sql_unique_query_number;
01421              x++, ptr_statement= ptr_statement->getNext())
01422         {
01423           ptr_statement->setNext(build_select_string(false));
01424         }
01425       }
01426       else if (sql_type->getString()[0] == 'k')
01427       {
01428         if (verbose >= 2)
01429           printf("Generating SELECT for keys Statements for Auto\n");
01430 
01431         if ( auto_generate_sql_autoincrement == false &&
01432              auto_generate_sql_guid_primary == false)
01433         {
01434           fprintf(stderr,
01435                   "%s: Can't perform key test without a primary key!\n",
01436                   SLAP_NAME);
01437           abort();
01438         }
01439 
01440         query_statements[sql_type_count]= build_select_string(true);
01441         for (ptr_statement= query_statements[sql_type_count], x= 0;
01442              x < auto_generate_sql_unique_query_number;
01443              x++, ptr_statement= ptr_statement->getNext())
01444         {
01445           ptr_statement->setNext(build_select_string(true));
01446         }
01447       }
01448       else if (sql_type->getString()[0] == 'w')
01449       {
01450         /*
01451           We generate a number of strings in case the engine is
01452           Archive (since strings which were identical one after another
01453           would be too easily optimized).
01454         */
01455         if (verbose >= 2)
01456           printf("Generating INSERT Statements for Auto\n");
01457         query_statements[sql_type_count]= build_insert_string();
01458         for (ptr_statement= query_statements[sql_type_count], x= 0;
01459              x < auto_generate_sql_unique_query_number;
01460              x++, ptr_statement= ptr_statement->getNext())
01461         {
01462           ptr_statement->setNext(build_insert_string());
01463         }
01464       }
01465       else if (sql_type->getString()[0] == 'u')
01466       {
01467         if ( auto_generate_sql_autoincrement == false &&
01468              auto_generate_sql_guid_primary == false)
01469         {
01470           fprintf(stderr,
01471                   "%s: Can't perform update test without a primary key!\n",
01472                   SLAP_NAME);
01473           abort();
01474         }
01475 
01476         query_statements[sql_type_count]= build_update_string();
01477         for (ptr_statement= query_statements[sql_type_count], x= 0;
01478              x < auto_generate_sql_unique_query_number;
01479              x++, ptr_statement= ptr_statement->getNext())
01480         {
01481           ptr_statement->setNext(build_update_string());
01482         }
01483       }
01484       else /* Mixed mode is default */
01485       {
01486         int coin= 0;
01487 
01488         query_statements[sql_type_count]= build_insert_string();
01489         /*
01490           This logic should be extended to do a more mixed load,
01491           at the moment it results in "every other".
01492         */
01493         for (ptr_statement= query_statements[sql_type_count], x= 0;
01494              x < auto_generate_sql_unique_query_number;
01495              x++, ptr_statement= ptr_statement->getNext())
01496         {
01497           if (coin)
01498           {
01499             ptr_statement->setNext(build_insert_string());
01500             coin= 0;
01501           }
01502           else
01503           {
01504             ptr_statement->setNext(build_select_string(true));
01505             coin= 1;
01506           }
01507         }
01508       }
01509       sql_type_count++;
01510     } while (sql_type ? (sql_type= sql_type->getNext()) : 0);
01511   }
01512   else
01513   {
01514     if (not create_string.empty() && !stat(create_string.c_str(), &sbuf))
01515     {
01516       int data_file;
01517       std::vector<char> tmp_string;
01518       if (not S_ISREG(sbuf.st_mode))
01519       {
01520         fprintf(stderr,"%s: Create file was not a regular file\n",
01521                 SLAP_NAME);
01522         abort();
01523       }
01524       if ((data_file= open(create_string.c_str(), O_RDWR)) == -1)
01525       {
01526         fprintf(stderr,"%s: Could not open create file\n", SLAP_NAME);
01527         abort();
01528       }
01529       if ((uint64_t)(sbuf.st_size + 1) > SIZE_MAX)
01530       {
01531         fprintf(stderr, "Request for more memory than architecture supports\n");
01532         abort();
01533       }
01534       tmp_string.resize(sbuf.st_size + 1);
01535       bytes_read= read(data_file, (unsigned char*) &tmp_string[0],
01536                        (size_t)sbuf.st_size);
01537       close(data_file);
01538       if (bytes_read != sbuf.st_size)
01539       {
01540         fprintf(stderr, "Problem reading file: read less bytes than requested\n");
01541       }
01542       parse_delimiter(&tmp_string[0], &create_statements, delimiter[0]);
01543     }
01544     else if (not create_string.empty())
01545     {
01546       parse_delimiter(create_string.c_str(), &create_statements, delimiter[0]);
01547     }
01548 
01549     /* Set this up till we fully support options on user generated queries */
01550     if (not user_supplied_query.empty())
01551     {
01552       query_statements_count=
01553         parse_option("default", &query_options, ',');
01554 
01555       query_statements.resize(query_statements_count);
01556     }
01557 
01558     if (not user_supplied_query.empty() && !stat(user_supplied_query.c_str(), &sbuf))
01559     {
01560       int data_file;
01561       std::vector<char> tmp_string;
01562 
01563       if (not S_ISREG(sbuf.st_mode))
01564       {
01565         fprintf(stderr,"%s: User query supplied file was not a regular file\n",
01566                 SLAP_NAME);
01567         abort();
01568       }
01569       if ((data_file= open(user_supplied_query.c_str(), O_RDWR)) == -1)
01570       {
01571         fprintf(stderr,"%s: Could not open query supplied file\n", SLAP_NAME);
01572         abort();
01573       }
01574       if ((uint64_t)(sbuf.st_size + 1) > SIZE_MAX)
01575       {
01576         fprintf(stderr, "Request for more memory than architecture supports\n");
01577         abort();
01578       }
01579       tmp_string.resize((size_t)(sbuf.st_size + 1));
01580       bytes_read= read(data_file, (unsigned char*) &tmp_string[0],
01581                        (size_t)sbuf.st_size);
01582       close(data_file);
01583       if (bytes_read != sbuf.st_size)
01584       {
01585         fprintf(stderr, "Problem reading file: read less bytes than requested\n");
01586       }
01587       if (not user_supplied_query.empty())
01588         actual_queries= parse_delimiter(&tmp_string[0], &query_statements[0],
01589                                         delimiter[0]);
01590     }
01591     else if (not user_supplied_query.empty())
01592     {
01593       actual_queries= parse_delimiter(user_supplied_query.c_str(), &query_statements[0],
01594                                       delimiter[0]);
01595     }
01596   }
01597 
01598   if (not user_supplied_pre_statements.empty()
01599       && !stat(user_supplied_pre_statements.c_str(), &sbuf))
01600   {
01601     int data_file;
01602     std::vector<char> tmp_string;
01603 
01604     if (not S_ISREG(sbuf.st_mode))
01605     {
01606       fprintf(stderr,"%s: User query supplied file was not a regular file\n",
01607               SLAP_NAME);
01608       abort();
01609     }
01610     if ((data_file= open(user_supplied_pre_statements.c_str(), O_RDWR)) == -1)
01611     {
01612       fprintf(stderr,"%s: Could not open query supplied file\n", SLAP_NAME);
01613       abort();
01614     }
01615     if ((uint64_t)(sbuf.st_size + 1) > SIZE_MAX)
01616     {
01617       fprintf(stderr, "Request for more memory than architecture supports\n");
01618       abort();
01619     }
01620     tmp_string.resize((size_t)(sbuf.st_size + 1));
01621     bytes_read= read(data_file, (unsigned char*) &tmp_string[0],
01622                      (size_t)sbuf.st_size);
01623     close(data_file);
01624     if (bytes_read != sbuf.st_size)
01625     {
01626       fprintf(stderr, "Problem reading file: read less bytes than requested\n");
01627     }
01628     if (not user_supplied_pre_statements.empty())
01629       (void)parse_delimiter(&tmp_string[0], &pre_statements,
01630                             delimiter[0]);
01631   }
01632   else if (not user_supplied_pre_statements.empty())
01633   {
01634     (void)parse_delimiter(user_supplied_pre_statements.c_str(),
01635                           &pre_statements,
01636                           delimiter[0]);
01637   }
01638 
01639   if (not user_supplied_post_statements.empty()
01640       && !stat(user_supplied_post_statements.c_str(), &sbuf))
01641   {
01642     int data_file;
01643     std::vector<char> tmp_string;
01644 
01645     if (not S_ISREG(sbuf.st_mode))
01646     {
01647       fprintf(stderr,"%s: User query supplied file was not a regular file\n",
01648               SLAP_NAME);
01649       abort();
01650     }
01651     if ((data_file= open(user_supplied_post_statements.c_str(), O_RDWR)) == -1)
01652     {
01653       fprintf(stderr,"%s: Could not open query supplied file\n", SLAP_NAME);
01654       abort();
01655     }
01656 
01657     if ((uint64_t)(sbuf.st_size + 1) > SIZE_MAX)
01658     {
01659       fprintf(stderr, "Request for more memory than architecture supports\n");
01660       abort();
01661     }
01662     tmp_string.resize((size_t)(sbuf.st_size + 1));
01663 
01664     bytes_read= read(data_file, (unsigned char*) &tmp_string[0],
01665                      (size_t)(sbuf.st_size));
01666     close(data_file);
01667     if (bytes_read != sbuf.st_size)
01668     {
01669       fprintf(stderr, "Problem reading file: read less bytes than requested\n");
01670     }
01671     if (not user_supplied_post_statements.empty())
01672       (void)parse_delimiter(&tmp_string[0], &post_statements,
01673                             delimiter[0]);
01674   }
01675   else if (not user_supplied_post_statements.empty())
01676   {
01677     (void)parse_delimiter(user_supplied_post_statements.c_str(), &post_statements,
01678                           delimiter[0]);
01679   }
01680 
01681   if (verbose >= 2)
01682     printf("Parsing engines to use.\n");
01683 
01684   if (not default_engine.empty())
01685     parse_option(default_engine.c_str(), &engine_options, ',');
01686 
01687   if (tty_password)
01688     opt_password= client_get_tty_password(NULL);
01689   return(0);
01690 }
01691 
01692 
01693 static int run_query(drizzle_con_st &con, drizzle_result_st *result,
01694                      const char *query, int len)
01695 {
01696   drizzle_return_t ret;
01697   drizzle_result_st result_buffer;
01698 
01699   if (opt_only_print)
01700   {
01701     printf("/* CON: %" PRIu64 " */ %.*s;\n",
01702            (uint64_t)drizzle_context(drizzle_con_drizzle(&con)),
01703            len, query);
01704     return 0;
01705   }
01706 
01707   if (verbose >= 3)
01708     printf("%.*s;\n", len, query);
01709 
01710   if (result == NULL)
01711     result= &result_buffer;
01712 
01713   result= drizzle_query(&con, result, query, len, &ret);
01714 
01715   if (ret == DRIZZLE_RETURN_OK)
01716     ret= drizzle_result_buffer(result);
01717 
01718   if (result == &result_buffer)
01719     drizzle_result_free(result);
01720     
01721   return ret;
01722 }
01723 
01724 
01725 static int
01726 generate_primary_key_list(drizzle_con_st &con, OptionString *engine_stmt)
01727 {
01728   drizzle_result_st result;
01729   drizzle_row_t row;
01730   uint64_t counter;
01731 
01732 
01733   /*
01734     Blackhole is a special case, this allows us to test the upper end
01735     of the server during load runs.
01736   */
01737   if (opt_only_print || (engine_stmt &&
01738                          strstr(engine_stmt->getString(), "blackhole")))
01739   {
01740     /* Yes, we strdup a const string to simplify the interface */
01741     primary_keys.push_back("796c4422-1d94-102a-9d6d-00e0812d");
01742   }
01743   else
01744   {
01745     if (run_query(con, &result, "SELECT id from t1", strlen("SELECT id from t1")))
01746     {
01747       fprintf(stderr,"%s: Cannot select GUID primary keys. (%s)\n", SLAP_NAME,
01748               drizzle_con_error(&con));
01749       abort();
01750     }
01751 
01752     uint64_t num_rows_ret= drizzle_result_row_count(&result);
01753     if (num_rows_ret > SIZE_MAX)
01754     {
01755       fprintf(stderr, "More primary keys than than architecture supports\n");
01756       abort();
01757     }
01758     size_t primary_keys_number_of;
01759     primary_keys_number_of= (size_t)num_rows_ret;
01760 
01761     /* So why check this? Blackhole :) */
01762     if (primary_keys_number_of)
01763     {
01764       /*
01765         We create the structure and loop and create the items.
01766       */
01767       row= drizzle_row_next(&result);
01768       for (counter= 0; counter < primary_keys_number_of;
01769            counter++, row= drizzle_row_next(&result))
01770       {
01771         primary_keys.push_back(row[0]);
01772       }
01773     }
01774 
01775     drizzle_result_free(&result);
01776   }
01777 
01778   return(0);
01779 }
01780 
01781 static void create_schema(drizzle_con_st &con, const char *db, Statement *stmt, OptionString *engine_stmt, Stats *sptr)
01782 {
01783   char query[HUGE_STRING_LENGTH];
01784   Statement *ptr;
01785   Statement *after_create;
01786   int len;
01787   struct timeval start_time, end_time;
01788 
01789 
01790   gettimeofday(&start_time, NULL);
01791 
01792   len= snprintf(query, HUGE_STRING_LENGTH, "CREATE SCHEMA `%s`", db);
01793 
01794   if (verbose >= 2)
01795     printf("Loading Pre-data\n");
01796 
01797   if (run_query(con, NULL, query, len))
01798   {
01799     fprintf(stderr,"%s: Cannot create schema %s : %s\n", SLAP_NAME, db,
01800             drizzle_con_error(&con));
01801     abort();
01802   }
01803   else
01804   {
01805     sptr->setCreateCount(sptr->getCreateCount()+1);
01806   }
01807 
01808   if (opt_only_print)
01809   {
01810     printf("/* CON: %" PRIu64 " */ use %s;\n",
01811            (uint64_t)drizzle_context(drizzle_con_drizzle(&con)),
01812            db);
01813   }
01814   else
01815   {
01816     drizzle_result_st result;
01817     drizzle_return_t ret;
01818 
01819     if (verbose >= 3)
01820       printf("%s;\n", query);
01821 
01822     if (drizzle_select_db(&con,  &result, db, &ret) == NULL ||
01823         ret != DRIZZLE_RETURN_OK)
01824     {
01825       fprintf(stderr,"%s: Cannot select schema '%s': %s\n",SLAP_NAME, db,
01826               ret == DRIZZLE_RETURN_ERROR_CODE ?
01827               drizzle_result_error(&result) : drizzle_con_error(&con));
01828       abort();
01829     }
01830     drizzle_result_free(&result);
01831     sptr->setCreateCount(sptr->getCreateCount()+1);
01832   }
01833 
01834   if (engine_stmt)
01835   {
01836     len= snprintf(query, HUGE_STRING_LENGTH, "set storage_engine=`%s`",
01837                   engine_stmt->getString());
01838     if (run_query(con, NULL, query, len))
01839     {
01840       fprintf(stderr,"%s: Cannot set default engine: %s\n", SLAP_NAME,
01841               drizzle_con_error(&con));
01842       abort();
01843     }
01844     sptr->setCreateCount(sptr->getCreateCount()+1);
01845   }
01846 
01847   uint64_t count= 0;
01848   after_create= stmt;
01849 
01850 limit_not_met:
01851   for (ptr= after_create; ptr && ptr->getLength(); ptr= ptr->getNext(), count++)
01852   {
01853     if (auto_generate_sql && ( auto_generate_sql_number == count))
01854       break;
01855 
01856     if (engine_stmt && engine_stmt->getOption() && ptr->getType() == CREATE_TABLE_TYPE)
01857     {
01858       char buffer[HUGE_STRING_LENGTH];
01859 
01860       snprintf(buffer, HUGE_STRING_LENGTH, "%s %s", ptr->getString(),
01861                engine_stmt->getOption());
01862       if (run_query(con, NULL, buffer, strlen(buffer)))
01863       {
01864         fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n",
01865                 SLAP_NAME, (uint32_t)ptr->getLength(), ptr->getString(), drizzle_con_error(&con));
01866         if (not opt_ignore_sql_errors)
01867           abort();
01868       }
01869       sptr->setCreateCount(sptr->getCreateCount()+1);
01870     }
01871     else
01872     {
01873       if (run_query(con, NULL, ptr->getString(), ptr->getLength()))
01874       {
01875         fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n",
01876                 SLAP_NAME, (uint32_t)ptr->getLength(), ptr->getString(), drizzle_con_error(&con));
01877         if (not opt_ignore_sql_errors)
01878           abort();
01879       }
01880       sptr->setCreateCount(sptr->getCreateCount()+1);
01881     }
01882   }
01883 
01884   if (auto_generate_sql && (auto_generate_sql_number > count ))
01885   {
01886     /* Special case for auto create, we don't want to create tables twice */
01887     after_create= stmt->getNext();
01888     goto limit_not_met;
01889   }
01890 
01891   gettimeofday(&end_time, NULL);
01892 
01893   sptr->setCreateTiming(timedif(end_time, start_time));
01894 }
01895 
01896 static void drop_schema(drizzle_con_st &con, const char *db)
01897 {
01898   char query[HUGE_STRING_LENGTH];
01899   int len;
01900 
01901   len= snprintf(query, HUGE_STRING_LENGTH, "DROP SCHEMA IF EXISTS `%s`", db);
01902 
01903   if (run_query(con, NULL, query, len))
01904   {
01905     fprintf(stderr,"%s: Cannot drop database '%s' ERROR : %s\n",
01906             SLAP_NAME, db, drizzle_con_error(&con));
01907     abort();
01908   }
01909 }
01910 
01911 static void run_statements(drizzle_con_st &con, Statement *stmt)
01912 {
01913   for (Statement *ptr= stmt; ptr && ptr->getLength(); ptr= ptr->getNext())
01914   {
01915     if (run_query(con, NULL, ptr->getString(), ptr->getLength()))
01916     {
01917       fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n",
01918               SLAP_NAME, (uint32_t)ptr->getLength(), ptr->getString(), drizzle_con_error(&con));
01919       abort();
01920     }
01921   }
01922 }
01923 
01924 
01925 static void timer_thread()
01926 {
01927   /*
01928     We lock around the initial call in case were we in a loop. This
01929     also keeps the value properly syncronized across call threads.
01930   */
01931   master_wakeup.wait();
01932 
01933   {
01934     boost::mutex::scoped_lock scopedLock(timer_alarm_mutex);
01935 
01936     boost::xtime xt; 
01937     xtime_get(&xt, boost::TIME_UTC); 
01938     xt.sec += opt_timer_length; 
01939 
01940     (void)timer_alarm_threshold.timed_wait(scopedLock, xt);
01941   }
01942 
01943   {
01944     boost::mutex::scoped_lock scopedLock(timer_alarm_mutex);
01945     timer_alarm= false;
01946   }
01947 }
01948 
01949 typedef boost::shared_ptr<boost::thread> Thread;
01950 typedef std::vector <Thread> Threads;
01951 static void run_scheduler(Stats *sptr, Statement **stmts, uint32_t concur, uint64_t limit)
01952 {
01953   uint32_t real_concurrency;
01954   struct timeval start_time, end_time;
01955 
01956   Threads threads;
01957 
01958   {
01959     OptionString *sql_type;
01960 
01961     master_wakeup.reset();
01962 
01963     real_concurrency= 0;
01964 
01965     uint32_t y;
01966     for (y= 0, sql_type= query_options;
01967          y < query_statements_count;
01968          y++, sql_type= sql_type->getNext())
01969     {
01970       uint32_t options_loop= 1;
01971 
01972       if (sql_type->getOption())
01973       {
01974         options_loop= strtol(sql_type->getOption(),
01975                              (char **)NULL, 10);
01976         options_loop= options_loop ? options_loop : 1;
01977       }
01978 
01979       while (options_loop--)
01980       {
01981         for (uint32_t x= 0; x < concur; x++)
01982         {
01983           ThreadContext *con;
01984           con= new ThreadContext;
01985           if (con == NULL)
01986           {
01987             fprintf(stderr, "Memory Allocation error in scheduler\n");
01988             abort();
01989           }
01990           con->setStmt(stmts[y]);
01991           con->setLimit(limit);
01992 
01993           real_concurrency++;
01994 
01995           /* now you create the thread */
01996           Thread thread;
01997           thread= Thread(new boost::thread(boost::bind(&run_task, con)));
01998           threads.push_back(thread);
01999 
02000         }
02001       }
02002     }
02003 
02004     /*
02005       The timer_thread belongs to all threads so it too obeys the wakeup
02006       call that run tasks obey.
02007     */
02008     if (opt_timer_length)
02009     {
02010       {
02011         boost::mutex::scoped_lock alarmLock(timer_alarm_mutex);
02012         timer_alarm= true;
02013       }
02014 
02015       Thread thread;
02016       thread= Thread(new boost::thread(&timer_thread));
02017       threads.push_back(thread);
02018     }
02019   }
02020 
02021   master_wakeup.start();
02022 
02023   gettimeofday(&start_time, NULL);
02024 
02025   /*
02026     We loop until we know that all children have cleaned up.
02027   */
02028   for (Threads::iterator iter= threads.begin(); iter != threads.end(); iter++)
02029   {
02030     (*iter)->join();
02031   }
02032 
02033   gettimeofday(&end_time, NULL);
02034 
02035   sptr->setTiming(timedif(end_time, start_time));
02036   sptr->setUsers(concur);
02037   sptr->setRealUsers(real_concurrency);
02038   sptr->setRows(limit);
02039 }
02040 
02041 /*
02042   Parse records from comma seperated string. : is a reserved character and is used for options
02043   on variables.
02044 */
02045 uint32_t parse_option(const char *origin, OptionString **stmt, char delm)
02046 {
02047   char *string;
02048   char *begin_ptr;
02049   char *end_ptr;
02050   uint32_t length= strlen(origin);
02051   uint32_t count= 0; /* We know that there is always one */
02052 
02053   end_ptr= (char *)origin + length;
02054 
02055   OptionString *tmp;
02056   *stmt= tmp= new OptionString;
02057 
02058   for (begin_ptr= (char *)origin;
02059        begin_ptr != end_ptr;
02060        tmp= tmp->getNext())
02061   {
02062     char buffer[HUGE_STRING_LENGTH];
02063     char *buffer_ptr;
02064 
02065     memset(buffer, 0, HUGE_STRING_LENGTH);
02066 
02067     string= strchr(begin_ptr, delm);
02068 
02069     if (string)
02070     {
02071       memcpy(buffer, begin_ptr, string - begin_ptr);
02072       begin_ptr= string+1;
02073     }
02074     else
02075     {
02076       size_t begin_len= strlen(begin_ptr);
02077       memcpy(buffer, begin_ptr, begin_len);
02078       begin_ptr= end_ptr;
02079     }
02080 
02081     if ((buffer_ptr= strchr(buffer, ':')))
02082     {
02083       /* Set a null so that we can get strlen() correct later on */
02084       buffer_ptr[0]= 0;
02085       buffer_ptr++;
02086 
02087       /* Move past the : and the first string */
02088       tmp->setOption(buffer_ptr);
02089     }
02090 
02091     tmp->setString(strdup(buffer));
02092     if (tmp->getString() == NULL)
02093     {
02094       fprintf(stderr,"Error allocating memory while parsing options\n");
02095       abort();
02096     }
02097 
02098     if (isspace(*begin_ptr))
02099       begin_ptr++;
02100 
02101     count++;
02102 
02103     if (begin_ptr != end_ptr)
02104     {
02105       tmp->setNext( new OptionString);
02106     }
02107     
02108   }
02109 
02110   return count;
02111 }
02112 
02113 
02114 /*
02115   Raw parsing interface. If you want the slap specific parser look at
02116   parse_option.
02117 */
02118 uint32_t parse_delimiter(const char *script, Statement **stmt, char delm)
02119 {
02120   char *retstr;
02121   char *ptr= (char *)script;
02122   Statement **sptr= stmt;
02123   Statement *tmp;
02124   uint32_t length= strlen(script);
02125   uint32_t count= 0; /* We know that there is always one */
02126 
02127   for (tmp= *sptr= new Statement;
02128        (retstr= strchr(ptr, delm));
02129        tmp->setNext(new Statement),
02130        tmp= tmp->getNext())
02131   {
02132     if (tmp == NULL)
02133     {
02134       fprintf(stderr,"Error allocating memory while parsing delimiter\n");
02135       abort();
02136     }
02137 
02138     count++;
02139     tmp->setString((size_t)(retstr - ptr));
02140 
02141     if (tmp->getString() == NULL)
02142     {
02143       fprintf(stderr,"Error allocating memory while parsing delimiter\n");
02144       abort();
02145     }
02146 
02147     memcpy(tmp->getString(), ptr, tmp->getLength());
02148     ptr+= retstr - ptr + 1;
02149     if (isspace(*ptr))
02150       ptr++;
02151   }
02152 
02153   if (ptr != script+length)
02154   {
02155     tmp->setString((size_t)((script + length) - ptr));
02156     if (tmp->getString() == NULL)
02157     {
02158       fprintf(stderr,"Error allocating memory while parsing delimiter\n");
02159       abort();
02160     }
02161     memcpy(tmp->getString(), ptr, tmp->getLength());
02162     count++;
02163   }
02164 
02165   return count;
02166 }
02167 
02168 
02169 /*
02170   Parse comma is different from parse_delimeter in that it parses
02171   number ranges from a comma seperated string.
02172   In restrospect, this is a lousy name from this function.
02173 */
02174 uint32_t parse_comma(const char *string, std::vector <uint32_t> &range)
02175 {
02176   uint32_t count= 1; /* We know that there is always one */
02177   char *retstr;
02178   char *ptr= (char *)string;
02179   uint32_t *nptr;
02180 
02181   for (;*ptr; ptr++)
02182     if (*ptr == ',') count++;
02183 
02184   /* One extra spot for the NULL */
02185   range.resize(count +1);
02186   nptr= &range[0];
02187 
02188   ptr= (char *)string;
02189   uint32_t x= 0;
02190   while ((retstr= strchr(ptr,',')))
02191   {
02192     nptr[x++]= atoi(ptr);
02193     ptr+= retstr - ptr + 1;
02194   }
02195   nptr[x++]= atoi(ptr);
02196 
02197   return count;
02198 }
02199 
02200 void print_conclusions(Conclusions &con)
02201 {
02202   printf("Benchmark\n");
02203   if (con.getEngine())
02204     printf("\tRunning for engine %s\n", con.getEngine());
02205 
02206   if (not opt_label.empty() || !opt_auto_generate_sql_type.empty())
02207   {
02208     const char *ptr= opt_auto_generate_sql_type.c_str() ? opt_auto_generate_sql_type.c_str() : "query";
02209     printf("\tLoad: %s\n", !opt_label.empty() ? opt_label.c_str() : ptr);
02210   }
02211   printf("\tAverage Time took to generate schema and initial data: %ld.%03ld seconds\n",
02212          con.getCreateAvgTiming() / 1000, con.getCreateAvgTiming() % 1000);
02213   printf("\tAverage number of seconds to run all queries: %ld.%03ld seconds\n",
02214          con.getAvgTiming() / 1000, con.getAvgTiming() % 1000);
02215   printf("\tMinimum number of seconds to run all queries: %ld.%03ld seconds\n",
02216          con.getMinTiming() / 1000, con.getMinTiming() % 1000);
02217   printf("\tMaximum number of seconds to run all queries: %ld.%03ld seconds\n",
02218          con.getMaxTiming() / 1000, con.getMaxTiming() % 1000);
02219   printf("\tTotal time for tests: %ld.%03ld seconds\n",
02220          con.getSumOfTime() / 1000, con.getSumOfTime() % 1000);
02221   printf("\tStandard Deviation: %ld.%03ld\n", con.getStdDev() / 1000, con.getStdDev() % 1000);
02222   printf("\tNumber of queries in create queries: %"PRIu64"\n", con.getCreateCount());
02223   printf("\tNumber of clients running queries: %u/%u\n",
02224          con.getUsers(), con.getRealUsers());
02225   printf("\tNumber of times test was run: %u\n", iterations);
02226   printf("\tAverage number of queries per client: %"PRIu64"\n", con.getAvgRows());
02227 
02228   uint64_t temp_val= failed_update_for_transaction; 
02229   if (temp_val)
02230     printf("\tFailed number of updates %"PRIu64"\n", temp_val);
02231 
02232   printf("\n");
02233 }
02234 
02235 void print_conclusions_csv(Conclusions &con)
02236 {
02237   char buffer[HUGE_STRING_LENGTH];
02238   char label_buffer[HUGE_STRING_LENGTH];
02239   size_t string_len;
02240   const char *temp_label= opt_label.c_str();
02241 
02242   memset(label_buffer, 0, sizeof(label_buffer));
02243 
02244   if (not opt_label.empty())
02245   {
02246     string_len= opt_label.length();
02247 
02248     for (uint32_t x= 0; x < string_len; x++)
02249     {
02250       if (temp_label[x] == ',')
02251         label_buffer[x]= '-';
02252       else
02253         label_buffer[x]= temp_label[x] ;
02254     }
02255   }
02256   else if (not opt_auto_generate_sql_type.empty())
02257   {
02258     string_len= opt_auto_generate_sql_type.length();
02259 
02260     for (uint32_t x= 0; x < string_len; x++)
02261     {
02262       if (opt_auto_generate_sql_type[x] == ',')
02263         label_buffer[x]= '-';
02264       else
02265         label_buffer[x]= opt_auto_generate_sql_type[x] ;
02266     }
02267   }
02268   else
02269   {
02270     snprintf(label_buffer, HUGE_STRING_LENGTH, "query");
02271   }
02272 
02273   snprintf(buffer, HUGE_STRING_LENGTH,
02274            "%s,%s,%ld.%03ld,%ld.%03ld,%ld.%03ld,%ld.%03ld,%ld.%03ld,"
02275            "%u,%u,%u,%"PRIu64"\n",
02276            con.getEngine() ? con.getEngine() : "", /* Storage engine we ran against */
02277            label_buffer, /* Load type */
02278            con.getAvgTiming() / 1000, con.getAvgTiming() % 1000, /* Time to load */
02279            con.getMinTiming() / 1000, con.getMinTiming() % 1000, /* Min time */
02280            con.getMaxTiming() / 1000, con.getMaxTiming() % 1000, /* Max time */
02281            con.getSumOfTime() / 1000, con.getSumOfTime() % 1000, /* Total time */
02282            con.getStdDev() / 1000, con.getStdDev() % 1000, /* Standard Deviation */
02283            iterations, /* Iterations */
02284            con.getUsers(), /* Children used max_timing */
02285            con.getRealUsers(), /* Children used max_timing */
02286            con.getAvgRows()  /* Queries run */
02287            );
02288   size_t buff_len= strlen(buffer);
02289   ssize_t write_ret= write(csv_file, (unsigned char*) buffer, buff_len);
02290   if (write_ret != (ssize_t)buff_len)
02291   {
02292     fprintf(stderr, _("Unable to fully write %"PRIu64" bytes. "
02293                       "Could only write %"PRId64"."), (uint64_t)write_ret,
02294                       (int64_t)buff_len);
02295     exit(-1);
02296   }
02297 }
02298 
02299 void generate_stats(Conclusions *con, OptionString *eng, Stats *sptr)
02300 {
02301   Stats *ptr;
02302   uint32_t x;
02303 
02304   con->setMinTiming(sptr->getTiming());
02305   con->setMaxTiming(sptr->getTiming());
02306   con->setMinRows(sptr->getRows());
02307   con->setMaxRows(sptr->getRows());
02308 
02309   /* At the moment we assume uniform */
02310   con->setUsers(sptr->getUsers());
02311   con->setRealUsers(sptr->getRealUsers());
02312   con->setAvgRows(sptr->getRows());
02313 
02314   /* With no next, we know it is the last element that was malloced */
02315   for (ptr= sptr, x= 0; x < iterations; ptr++, x++)
02316   {
02317     con->setAvgTiming(ptr->getTiming()+con->getAvgTiming());
02318 
02319     if (ptr->getTiming() > con->getMaxTiming())
02320       con->setMaxTiming(ptr->getTiming());
02321     if (ptr->getTiming() < con->getMinTiming())
02322       con->setMinTiming(ptr->getTiming());
02323   }
02324   con->setSumOfTime(con->getAvgTiming());
02325   con->setAvgTiming(con->getAvgTiming()/iterations);
02326 
02327   if (eng && eng->getString())
02328     con->setEngine(eng->getString());
02329   else
02330     con->setEngine(NULL);
02331 
02332   standard_deviation(*con, sptr);
02333 
02334   /* Now we do the create time operations */
02335   con->setCreateMinTiming(sptr->getCreateTiming());
02336   con->setCreateMaxTiming(sptr->getCreateTiming());
02337 
02338   /* At the moment we assume uniform */
02339   con->setCreateCount(sptr->getCreateCount());
02340 
02341   /* With no next, we know it is the last element that was malloced */
02342   for (ptr= sptr, x= 0; x < iterations; ptr++, x++)
02343   {
02344     con->setCreateAvgTiming(ptr->getCreateTiming()+con->getCreateAvgTiming());
02345 
02346     if (ptr->getCreateTiming() > con->getCreateMaxTiming())
02347       con->setCreateMaxTiming(ptr->getCreateTiming());
02348     if (ptr->getCreateTiming() < con->getCreateMinTiming())
02349       con->setCreateMinTiming(ptr->getCreateTiming());
02350   }
02351   con->setCreateAvgTiming(con->getCreateAvgTiming()/iterations);
02352 }
02353 
02354 void
02355 option_cleanup(OptionString *stmt)
02356 {
02357   OptionString *ptr, *nptr;
02358   if (not stmt)
02359     return;
02360 
02361   for (ptr= stmt; ptr; ptr= nptr)
02362   {
02363     nptr= ptr->getNext();
02364     delete ptr;
02365   }
02366 }
02367 
02368 void statement_cleanup(Statement *stmt)
02369 {
02370   Statement *ptr, *nptr;
02371   if (not stmt)
02372     return;
02373 
02374   for (ptr= stmt; ptr; ptr= nptr)
02375   {
02376     nptr= ptr->getNext();
02377     delete ptr;
02378   }
02379 }
02380 
02381 void slap_close(drizzle_con_st *con)
02382 {
02383   drizzle_free(drizzle_con_drizzle(con));
02384 }
02385 
02386 drizzle_con_st* slap_connect(bool connect_to_schema)
02387 {
02388   /* Connect to server */
02389   static uint32_t connection_retry_sleep= 100000; /* Microseconds */
02390   int connect_error= 1;
02391   drizzle_return_t ret;
02392   drizzle_st *drizzle;
02393 
02394   if (opt_delayed_start)
02395     usleep(random()%opt_delayed_start);
02396 
02397   drizzle_con_st* con;
02398   if ((drizzle= drizzle_create()) == NULL or
02399       (con= drizzle_con_add_tcp(drizzle,
02400                                 host.c_str(), opt_drizzle_port,
02401                                 user.c_str(), opt_password.c_str(),
02402                                 connect_to_schema ? create_schema_string.c_str() : NULL,
02403                                 use_drizzle_protocol ? DRIZZLE_CON_EXPERIMENTAL : DRIZZLE_CON_MYSQL)) == NULL)
02404   {
02405     fprintf(stderr,"%s: Error creating drizzle object\n", SLAP_NAME);
02406     abort();
02407   }
02408 
02409   drizzle_set_context(drizzle, (void*)(connection_count.fetch_and_increment()));
02410 
02411   if (opt_only_print)
02412   {
02413     return con;
02414   }
02415 
02416   for (uint32_t x= 0; x < 10; x++)
02417   {
02418     if ((ret= drizzle_con_connect(con)) == DRIZZLE_RETURN_OK)
02419     {
02420       /* Connect suceeded */
02421       connect_error= 0;
02422       break;
02423     }
02424     usleep(connection_retry_sleep);
02425   }
02426   if (connect_error)
02427   {
02428     fprintf(stderr,"%s: Error when connecting to server: %d %s\n", SLAP_NAME,
02429             ret, drizzle_con_error(con));
02430     abort();
02431   }
02432 
02433   return con;
02434 }
02435 
02436 void standard_deviation(Conclusions &con, Stats *sptr)
02437 {
02438   long int sum_of_squares;
02439   double the_catch;
02440   Stats *ptr;
02441 
02442   if (iterations == 1 || iterations == 0)
02443   {
02444     con.setStdDev(0);
02445     return;
02446   }
02447 
02448   uint32_t x;
02449   for (ptr= sptr, x= 0, sum_of_squares= 0; x < iterations; ptr++, x++)
02450   {
02451     long int deviation;
02452 
02453     deviation= ptr->getTiming() - con.getAvgTiming();
02454     sum_of_squares+= deviation*deviation;
02455   }
02456 
02457   the_catch= sqrt((double)(sum_of_squares/(iterations -1)));
02458   con.setStdDev((long int)the_catch);
02459 }