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_priv.h"
00076 
00077 #include "option_string.h"
00078 #include "stats.h"
00079 #include "thread_context.h"
00080 #include "conclusions.h"
00081 #include "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/thread.hpp>
00104 #include <boost/thread/mutex.hpp>
00105 #include <boost/thread/condition_variable.hpp>
00106 #include <boost/program_options.hpp>
00107 #include <boost/scoped_ptr.hpp>
00108 #include <drizzled/atomics.h>
00109 
00110 #define SLAP_NAME "drizzleslap"
00111 #define SLAP_VERSION "1.5"
00112 
00113 #define HUGE_STRING_LENGTH 8196
00114 #define RAND_STRING_SIZE 126
00115 #define DEFAULT_BLOB_SIZE 1024
00116 
00117 using namespace std;
00118 using namespace drizzled;
00119 namespace po= boost::program_options;
00120 
00121 #ifdef HAVE_SMEM
00122 static char *shared_memory_base_name=0;
00123 #endif
00124 
00125 client::Wakeup master_wakeup;
00126 
00127 /* Global Thread timer */
00128 static bool timer_alarm= false;
00129 boost::mutex timer_alarm_mutex;
00130 boost::condition_variable_any timer_alarm_threshold;
00131 
00132 std::vector < std::string > primary_keys;
00133 
00134 drizzled::atomic<size_t> connection_count;
00135 drizzled::atomic<uint64_t> failed_update_for_transaction;
00136 
00137 static string host, 
00138   opt_password, 
00139   user,
00140   user_supplied_query,
00141   user_supplied_pre_statements,
00142   user_supplied_post_statements,
00143   default_engine,
00144   pre_system,
00145   post_system;
00146 
00147 static vector<string> user_supplied_queries;
00148 static string opt_verbose;
00149 std::string opt_protocol;
00150 string delimiter;
00151 
00152 string create_schema_string;
00153 
00154 static bool use_drizzle_protocol= false;
00155 static bool opt_preserve= true;
00156 static bool opt_only_print;
00157 static bool opt_burnin;
00158 static bool opt_ignore_sql_errors= false;
00159 static bool opt_silent,
00160   auto_generate_sql_autoincrement,
00161   auto_generate_sql_guid_primary,
00162   auto_generate_sql;
00163 std::string opt_auto_generate_sql_type;
00164 
00165 static int32_t verbose= 0;
00166 static uint32_t delimiter_length;
00167 static uint32_t commit_rate;
00168 static uint32_t detach_rate;
00169 static uint32_t opt_timer_length;
00170 static uint32_t opt_delayed_start;
00171 string num_blob_cols_opt,
00172   num_char_cols_opt,
00173   num_int_cols_opt;
00174 string opt_label;
00175 static uint32_t opt_set_random_seed;
00176 
00177 string auto_generate_selected_columns_opt;
00178 
00179 /* Yes, we do set defaults here */
00180 static uint32_t num_int_cols= 1;
00181 static uint32_t num_char_cols= 1;
00182 static uint32_t num_blob_cols= 0;
00183 static uint32_t num_blob_cols_size;
00184 static uint32_t num_blob_cols_size_min;
00185 static uint32_t num_int_cols_index= 0;
00186 static uint32_t num_char_cols_index= 0;
00187 static uint32_t iterations;
00188 static uint64_t actual_queries= 0;
00189 static uint64_t auto_actual_queries;
00190 static uint64_t auto_generate_sql_unique_write_number;
00191 static uint64_t auto_generate_sql_unique_query_number;
00192 static uint32_t auto_generate_sql_secondary_indexes;
00193 static uint64_t num_of_query;
00194 static uint64_t auto_generate_sql_number;
00195 string concurrency_str;
00196 string create_string;
00197 std::vector <uint32_t> concurrency;
00198 
00199 std::string opt_csv_str;
00200 int csv_file;
00201 
00202 static int process_options(void);
00203 static uint32_t opt_drizzle_port= 0;
00204 
00205 static OptionString *engine_options= NULL;
00206 static OptionString *query_options= NULL;
00207 static Statement *pre_statements= NULL;
00208 static Statement *post_statements= NULL;
00209 static Statement *create_statements= NULL;
00210 
00211 static std::vector <Statement *> query_statements;
00212 static uint32_t query_statements_count;
00213 
00214 
00215 /* Prototypes */
00216 void print_conclusions(Conclusions &con);
00217 void print_conclusions_csv(Conclusions &con);
00218 void generate_stats(Conclusions *con, OptionString *eng, Stats *sptr);
00219 uint32_t parse_comma(const char *string, std::vector <uint32_t> &range);
00220 uint32_t parse_delimiter(const char *script, Statement **stmt, char delm);
00221 uint32_t parse_option(const char *origin, OptionString **stmt, char delm);
00222 static void drop_schema(drizzle_con_st &con, const char *db);
00223 uint32_t get_random_string(char *buf, size_t size);
00224 static Statement *build_table_string(void);
00225 static Statement *build_insert_string(void);
00226 static Statement *build_update_string(void);
00227 static Statement * build_select_string(bool key);
00228 static int generate_primary_key_list(drizzle_con_st &con, OptionString *engine_stmt);
00229 static void create_schema(drizzle_con_st &con, const char *db, Statement *stmt, OptionString *engine_stmt, Stats *sptr);
00230 static void run_scheduler(Stats *sptr, Statement **stmts, uint32_t concur, uint64_t limit);
00231 void statement_cleanup(Statement *stmt);
00232 void option_cleanup(OptionString *stmt);
00233 void concurrency_loop(drizzle_con_st &con, uint32_t current, OptionString *eptr);
00234 static void run_statements(drizzle_con_st &con, Statement *stmt);
00235 void slap_connect(drizzle_con_st &con, bool connect_to_schema);
00236 void slap_close(drizzle_con_st &con);
00237 static int run_query(drizzle_con_st &con, drizzle_result_st *result, const char *query, int len);
00238 void standard_deviation(Conclusions &con, Stats *sptr);
00239 
00240 static const char ALPHANUMERICS[]=
00241 "0123456789ABCDEFGHIJKLMNOPQRSTWXYZabcdefghijklmnopqrstuvwxyz";
00242 
00243 #define ALPHANUMERICS_SIZE (sizeof(ALPHANUMERICS)-1)
00244 
00245 
00246 static long int timedif(struct timeval a, struct timeval b)
00247 {
00248   int us, s;
00249 
00250   us = a.tv_usec - b.tv_usec;
00251   us /= 1000;
00252   s = a.tv_sec - b.tv_sec;
00253   s *= 1000;
00254   return s + us;
00255 }
00256 
00257 static void combine_queries(vector<string> queries)
00258 {
00259   user_supplied_query.erase();
00260   for (vector<string>::iterator it= queries.begin();
00261        it != queries.end();
00262        ++it)
00263   {
00264     user_supplied_query.append(*it);
00265     user_supplied_query.append(delimiter);
00266   }
00267 }
00268 
00269 
00270 static void run_task(ThreadContext *ctx)
00271 {
00272   uint64_t counter= 0, queries;
00273   uint64_t detach_counter;
00274   uint32_t commit_counter;
00275   boost::scoped_ptr<drizzle_con_st> con_ap(new drizzle_con_st);
00276   drizzle_con_st &con= *con_ap.get();
00277   drizzle_result_st result;
00278   drizzle_row_t row;
00279   Statement *ptr;
00280 
00281   master_wakeup.wait();
00282 
00283   slap_connect(con, true);
00284 
00285   if (verbose >= 3)
00286     printf("connected!\n");
00287   queries= 0;
00288 
00289   commit_counter= 0;
00290   if (commit_rate)
00291     run_query(con, NULL, "SET AUTOCOMMIT=0", strlen("SET AUTOCOMMIT=0"));
00292 
00293 limit_not_met:
00294   for (ptr= ctx->getStmt(), detach_counter= 0;
00295        ptr && ptr->getLength();
00296        ptr= ptr->getNext(), detach_counter++)
00297   {
00298     if (not opt_only_print && detach_rate && !(detach_counter % detach_rate))
00299     {
00300       slap_close(con);
00301       slap_connect(con, true);
00302     }
00303 
00304     /*
00305       We have to execute differently based on query type. This should become a function.
00306     */
00307     bool is_failed_update= false;
00308     if ((ptr->getType() == UPDATE_TYPE_REQUIRES_PREFIX) ||
00309         (ptr->getType() == SELECT_TYPE_REQUIRES_PREFIX))
00310     {
00311       int length;
00312       uint32_t key_val;
00313       char buffer[HUGE_STRING_LENGTH];
00314 
00315       /*
00316         This should only happen if some sort of new engine was
00317         implemented that didn't properly handle UPDATEs.
00318 
00319         Just in case someone runs this under an experimental engine we don't
00320         want a crash so the if() is placed here.
00321       */
00322       assert(primary_keys.size());
00323       if (primary_keys.size())
00324       {
00325         key_val= (uint32_t)(random() % primary_keys.size());
00326         const char *key;
00327         key= primary_keys[key_val].c_str();
00328 
00329         assert(key);
00330 
00331         length= snprintf(buffer, HUGE_STRING_LENGTH, "%.*s '%s'",
00332                          (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     run_query(con, NULL, "COMMIT", strlen("COMMIT"));
00407 
00408   slap_close(con);
00409 
00410   delete ctx;
00411 }
00412 
00431 int main(int argc, char **argv)
00432 {
00433   char *password= NULL;
00434   try
00435   {
00436     po::options_description commandline_options("Options used only in command line");
00437     commandline_options.add_options()
00438       ("help,?","Display this help and exit")
00439       ("info","Gives information and exit")
00440       ("burnin",po::value<bool>(&opt_burnin)->default_value(false)->zero_tokens(),
00441        "Run full test case in infinite loop")
00442       ("ignore-sql-errors", po::value<bool>(&opt_ignore_sql_errors)->default_value(false)->zero_tokens(),
00443        "Ignore SQL errors in query run")
00444       ("create-schema",po::value<string>(&create_schema_string)->default_value("drizzleslap"),
00445        "Schema to run tests in")
00446       ("create",po::value<string>(&create_string)->default_value(""),
00447        "File or string to use to create tables")
00448       ("detach",po::value<uint32_t>(&detach_rate)->default_value(0),
00449        "Detach (close and re open) connections after X number of requests")
00450       ("iterations,i",po::value<uint32_t>(&iterations)->default_value(1),
00451        "Number of times to run the tests")
00452       ("label",po::value<string>(&opt_label)->default_value(""),
00453        "Label to use for print and csv")
00454       ("number-blob-cols",po::value<string>(&num_blob_cols_opt)->default_value(""),
00455        "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. ")
00456       ("number-char-cols,x",po::value<string>(&num_char_cols_opt)->default_value(""),
00457        "Number of VARCHAR columns to create in table if specifying --auto-generate-sql.")
00458       ("number-int-cols,y",po::value<string>(&num_int_cols_opt)->default_value(""),
00459        "Number of INT columns to create in table if specifying --auto-generate-sql.")
00460       ("number-of-queries",
00461        po::value<uint64_t>(&num_of_query)->default_value(0),
00462        "Limit each client to this number of queries(this is not exact)") 
00463       ("only-print",po::value<bool>(&opt_only_print)->default_value(false)->zero_tokens(),
00464        "This causes drizzleslap to not connect to the database instead print out what it would have done instead")
00465       ("post-query", po::value<string>(&user_supplied_post_statements)->default_value(""),
00466        "Query to run or file containing query to execute after tests have completed.")
00467       ("post-system",po::value<string>(&post_system)->default_value(""),
00468        "system() string to execute after tests have completed")
00469       ("pre-query",
00470        po::value<string>(&user_supplied_pre_statements)->default_value(""),
00471        "Query to run or file containing query to execute before running tests.")
00472       ("pre-system",po::value<string>(&pre_system)->default_value(""),
00473        "system() string to execute before running tests.")
00474       ("query,q",po::value<vector<string> >(&user_supplied_queries)->composing()->notifier(&combine_queries),
00475        "Query to run or file containing query")
00476       ("verbose,v", po::value<string>(&opt_verbose)->default_value("v"), "Increase verbosity level by one.")
00477       ("version,V","Output version information and exit") 
00478       ;
00479 
00480     po::options_description slap_options("Options specific to drizzleslap");
00481     slap_options.add_options()
00482       ("auto-generate-sql-select-columns",
00483        po::value<string>(&auto_generate_selected_columns_opt)->default_value(""),
00484        "Provide a string to use for the select fields used in auto tests")
00485       ("auto-generate-sql,a",po::value<bool>(&auto_generate_sql)->default_value(false)->zero_tokens(),
00486        "Generate SQL where not supplied by file or command line")  
00487       ("auto-generate-sql-add-autoincrement",
00488        po::value<bool>(&auto_generate_sql_autoincrement)->default_value(false)->zero_tokens(),
00489        "Add an AUTO_INCREMENT column to auto-generated tables")
00490       ("auto-generate-sql-execute-number",
00491        po::value<uint64_t>(&auto_actual_queries)->default_value(0),
00492        "See this number and generate a set of queries to run")
00493       ("auto-generate-sql-guid-primary",
00494        po::value<bool>(&auto_generate_sql_guid_primary)->default_value(false)->zero_tokens(),
00495        "Add GUID based primary keys to auto-generated tables")
00496       ("auto-generate-sql-load-type",
00497        po::value<string>(&opt_auto_generate_sql_type)->default_value("mixed"),
00498        "Specify test load type: mixed, update, write, key or read; default is mixed")  
00499       ("auto-generate-sql-secondary-indexes",
00500        po::value<uint32_t>(&auto_generate_sql_secondary_indexes)->default_value(0),
00501        "Number of secondary indexes to add to auto-generated tables")
00502       ("auto-generated-sql-unique-query-number",
00503        po::value<uint64_t>(&auto_generate_sql_unique_query_number)->default_value(10),
00504        "Number of unique queries to generate for automatic tests")
00505       ("auto-generate-sql-unique-write-number",
00506        po::value<uint64_t>(&auto_generate_sql_unique_write_number)->default_value(10),
00507        "Number of unique queries to generate for auto-generate-sql-write-number")
00508       ("auto-generate-sql-write-number",
00509        po::value<uint64_t>(&auto_generate_sql_number)->default_value(100),
00510        "Number of row inserts to perform for each thread (default is 100).")
00511       ("commit",po::value<uint32_t>(&commit_rate)->default_value(0),
00512        "Commit records every X number of statements")
00513       ("concurrency,c",po::value<string>(&concurrency_str)->default_value(""),
00514        "Number of clients to simulate for query to run")
00515       ("csv",po::value<std::string>(&opt_csv_str)->default_value(""),
00516        "Generate CSV output to named file or to stdout if no file is name.")
00517       ("delayed-start",po::value<uint32_t>(&opt_delayed_start)->default_value(0),
00518        "Delay the startup of threads by a random number of microsends (the maximum of the delay")
00519       ("delimiter,F",po::value<string>(&delimiter)->default_value("\n"),
00520        "Delimiter to use in SQL statements supplied in file or command line")
00521       ("engine,e",po::value<string>(&default_engine)->default_value(""),
00522        "Storage engine to use for creating the table")
00523       ("set-random-seed",
00524        po::value<uint32_t>(&opt_set_random_seed)->default_value(0), 
00525        "Seed for random number generator (srandom(3)) ") 
00526       ("silent,s",po::value<bool>(&opt_silent)->default_value(false)->zero_tokens(),
00527        "Run program in silent mode - no output. ") 
00528       ("timer-length",po::value<uint32_t>(&opt_timer_length)->default_value(0),
00529        "Require drizzleslap to run each specific test a certain amount of time in seconds")  
00530       ;
00531 
00532     po::options_description client_options("Options specific to the client");
00533     client_options.add_options()
00534       ("host,h",po::value<string>(&host)->default_value("localhost"),"Connect to the host")
00535       ("password,P",po::value<char *>(&password),
00536        "Password to use when connecting to server. If password is not given it's asked from the tty")
00537       ("port,p",po::value<uint32_t>(), "Port number to use for connection")
00538       ("protocol",po::value<string>(&opt_protocol)->default_value("mysql"),
00539        "The protocol of connection (mysql or drizzle).")
00540       ("user,u",po::value<string>(&user)->default_value(""),
00541        "User for login if not current user")  
00542       ;
00543 
00544     po::options_description long_options("Allowed Options");
00545     long_options.add(commandline_options).add(slap_options).add(client_options);
00546 
00547     std::string system_config_dir_slap(SYSCONFDIR); 
00548     system_config_dir_slap.append("/drizzle/drizzleslap.cnf");
00549 
00550     std::string system_config_dir_client(SYSCONFDIR); 
00551     system_config_dir_client.append("/drizzle/client.cnf");
00552 
00553     std::string user_config_dir((getenv("XDG_CONFIG_HOME")? getenv("XDG_CONFIG_HOME"):"~/.config"));
00554 
00555     if (user_config_dir.compare(0, 2, "~/") == 0)
00556     {
00557       char *homedir;
00558       homedir= getenv("HOME");
00559       if (homedir != NULL)
00560         user_config_dir.replace(0, 1, homedir);
00561     }
00562 
00563     uint64_t temp_drizzle_port= 0;
00564     boost::scoped_ptr<drizzle_con_st> con_ap(new drizzle_con_st);
00565     drizzle_con_st &con= *con_ap.get();
00566     OptionString *eptr;
00567 
00568     // Disable allow_guessing
00569     int style = po::command_line_style::default_style & ~po::command_line_style::allow_guessing;
00570 
00571     po::variables_map vm;
00572     po::store(po::command_line_parser(argc, argv).options(long_options).
00573               style(style).extra_parser(parse_password_arg).run(), vm);
00574 
00575     std::string user_config_dir_slap(user_config_dir);
00576     user_config_dir_slap.append("/drizzle/drizzleslap.cnf"); 
00577 
00578     std::string user_config_dir_client(user_config_dir);
00579     user_config_dir_client.append("/drizzle/client.cnf");
00580 
00581     ifstream user_slap_ifs(user_config_dir_slap.c_str());
00582     po::store(parse_config_file(user_slap_ifs, slap_options), vm);
00583 
00584     ifstream user_client_ifs(user_config_dir_client.c_str());
00585     po::store(parse_config_file(user_client_ifs, client_options), vm);
00586 
00587     ifstream system_slap_ifs(system_config_dir_slap.c_str());
00588     store(parse_config_file(system_slap_ifs, slap_options), vm);
00589 
00590     ifstream system_client_ifs(system_config_dir_client.c_str());
00591     store(parse_config_file(system_client_ifs, client_options), vm);
00592 
00593     po::notify(vm);
00594 
00595     if (process_options())
00596       abort();
00597 
00598     if ( vm.count("help") || vm.count("info"))
00599     {
00600       printf("%s  Ver %s Distrib %s, for %s-%s (%s)\n",SLAP_NAME, SLAP_VERSION,
00601           drizzle_version(),HOST_VENDOR,HOST_OS,HOST_CPU);
00602       puts("Copyright (C) 2008 Sun Microsystems");
00603       puts("This software comes with ABSOLUTELY NO WARRANTY. "
00604            "This is free software,\n"
00605            "and you are welcome to modify and redistribute it under the GPL "
00606            "license\n");
00607       puts("Run a query multiple times against the server\n");
00608       cout << long_options << endl;
00609       abort();
00610     }   
00611 
00612     if (vm.count("protocol"))
00613     {
00614       std::transform(opt_protocol.begin(), opt_protocol.end(),
00615         opt_protocol.begin(), ::tolower);
00616 
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     slap_connect(con, 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       goto burnin;
00715 
00716     slap_close(con);
00717 
00718     /* now free all the strings we created */
00719     if (not opt_password.empty())
00720       opt_password.erase();
00721 
00722     concurrency.clear();
00723 
00724     statement_cleanup(create_statements);
00725     for (uint32_t x= 0; x < query_statements_count; x++)
00726       statement_cleanup(query_statements[x]);
00727     query_statements.clear();
00728     statement_cleanup(pre_statements);
00729     statement_cleanup(post_statements);
00730     option_cleanup(engine_options);
00731     option_cleanup(query_options);
00732 
00733 #ifdef HAVE_SMEM
00734     if (shared_memory_base_name)
00735       free(shared_memory_base_name);
00736 #endif
00737 
00738   }
00739 
00740   catch(std::exception &err)
00741   {
00742     cerr<<"Error:"<<err.what()<<endl;
00743   }
00744 
00745   if (csv_file != fileno(stdout))
00746     close(csv_file);
00747 
00748   return 0;
00749 }
00750 
00751 void concurrency_loop(drizzle_con_st &con, uint32_t current, OptionString *eptr)
00752 {
00753   Stats *head_sptr;
00754   Stats *sptr;
00755   Conclusions conclusion;
00756   uint64_t client_limit;
00757 
00758   head_sptr= new Stats[iterations];
00759   if (head_sptr == NULL)
00760   {
00761     fprintf(stderr,"Error allocating memory in concurrency_loop\n");
00762     abort();
00763   }
00764 
00765   if (auto_actual_queries)
00766     client_limit= auto_actual_queries;
00767   else if (num_of_query)
00768     client_limit=  num_of_query / current;
00769   else
00770     client_limit= actual_queries;
00771 
00772   uint32_t x;
00773   for (x= 0, sptr= head_sptr; x < iterations; x++, sptr++)
00774   {
00775     /*
00776       We might not want to load any data, such as when we are calling
00777       a stored_procedure that doesn't use data, or we know we already have
00778       data in the table.
00779     */
00780     if (opt_preserve == false)
00781       drop_schema(con, create_schema_string.c_str());
00782 
00783     /* First we create */
00784     if (create_statements)
00785       create_schema(con, create_schema_string.c_str(), create_statements, eptr, sptr);
00786 
00787     /*
00788       If we generated GUID we need to build a list of them from creation that
00789       we can later use.
00790     */
00791     if (verbose >= 2)
00792       printf("Generating primary key list\n");
00793     if (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary)
00794       generate_primary_key_list(con, eptr);
00795 
00796     if (not pre_system.empty())
00797     {
00798       int ret= system(pre_system.c_str());
00799       assert(ret != -1);
00800     }
00801 
00802     /*
00803       Pre statements are always run after all other logic so they can
00804       correct/adjust any item that they want.
00805     */
00806     if (pre_statements)
00807       run_statements(con, pre_statements);
00808 
00809     run_scheduler(sptr, &query_statements[0], current, client_limit);
00810 
00811     if (post_statements)
00812       run_statements(con, post_statements);
00813 
00814     if (not post_system.empty())
00815     {
00816       int ret=  system(post_system.c_str());
00817       assert(ret !=-1);
00818     }
00819 
00820     /* We are finished with this run */
00821     if (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary)
00822       primary_keys.clear();
00823   }
00824 
00825   if (verbose >= 2)
00826     printf("Generating stats\n");
00827 
00828   generate_stats(&conclusion, eptr, head_sptr);
00829 
00830   if (not opt_silent)
00831     print_conclusions(conclusion);
00832   if (not opt_csv_str.empty())
00833     print_conclusions_csv(conclusion);
00834 
00835   delete [] head_sptr;
00836 }
00837 
00838 
00839 uint32_t get_random_string(char *buf, size_t size)
00840 {
00841   char *buf_ptr= buf;
00842 
00843   for (size_t x= size; x > 0; x--)
00844     *buf_ptr++= ALPHANUMERICS[random() % ALPHANUMERICS_SIZE];
00845   return(buf_ptr - buf);
00846 }
00847 
00848 
00849 /*
00850   build_table_string
00851 
00852   This function builds a create table query if the user opts to not supply
00853   a file or string containing a create table statement
00854 */
00855 static Statement *
00856 build_table_string(void)
00857 {
00858   char       buf[HUGE_STRING_LENGTH];
00859   uint32_t        col_count;
00860   Statement *ptr;
00861   string table_string;
00862 
00863   table_string.reserve(HUGE_STRING_LENGTH);
00864 
00865   table_string= "CREATE TABLE `t1` (";
00866 
00867   if (auto_generate_sql_autoincrement)
00868   {
00869     table_string.append("id serial");
00870 
00871     if (num_int_cols || num_char_cols)
00872       table_string.append(",");
00873   }
00874 
00875   if (auto_generate_sql_guid_primary)
00876   {
00877     table_string.append("id varchar(128) primary key");
00878 
00879     if (num_int_cols || num_char_cols || auto_generate_sql_guid_primary)
00880       table_string.append(",");
00881   }
00882 
00883   if (auto_generate_sql_secondary_indexes)
00884   {
00885     for (uint32_t count= 0; count < auto_generate_sql_secondary_indexes; count++)
00886     {
00887       if (count) /* Except for the first pass we add a comma */
00888         table_string.append(",");
00889 
00890       if (snprintf(buf, HUGE_STRING_LENGTH, "id%d varchar(32) unique key", count)
00891           > HUGE_STRING_LENGTH)
00892       {
00893         fprintf(stderr, "Memory Allocation error in create table\n");
00894         abort();
00895       }
00896       table_string.append(buf);
00897     }
00898 
00899     if (num_int_cols || num_char_cols)
00900       table_string.append(",");
00901   }
00902 
00903   if (num_int_cols)
00904     for (col_count= 1; col_count <= num_int_cols; col_count++)
00905     {
00906       if (num_int_cols_index)
00907       {
00908         if (snprintf(buf, HUGE_STRING_LENGTH, "intcol%d INT, INDEX(intcol%d)",
00909                      col_count, col_count) > HUGE_STRING_LENGTH)
00910         {
00911           fprintf(stderr, "Memory Allocation error in create table\n");
00912           abort();
00913         }
00914       }
00915       else
00916       {
00917         if (snprintf(buf, HUGE_STRING_LENGTH, "intcol%d INT ", col_count)
00918             > HUGE_STRING_LENGTH)
00919         {
00920           fprintf(stderr, "Memory Allocation error in create table\n");
00921           abort();
00922         }
00923       }
00924       table_string.append(buf);
00925 
00926       if (col_count < num_int_cols || num_char_cols > 0)
00927         table_string.append(",");
00928     }
00929 
00930   if (num_char_cols)
00931     for (col_count= 1; col_count <= num_char_cols; col_count++)
00932     {
00933       if (num_char_cols_index)
00934       {
00935         if (snprintf(buf, HUGE_STRING_LENGTH,
00936                      "charcol%d VARCHAR(128), INDEX(charcol%d) ",
00937                      col_count, col_count) > HUGE_STRING_LENGTH)
00938         {
00939           fprintf(stderr, "Memory Allocation error in creating table\n");
00940           abort();
00941         }
00942       }
00943       else
00944       {
00945         if (snprintf(buf, HUGE_STRING_LENGTH, "charcol%d VARCHAR(128)",
00946                      col_count) > HUGE_STRING_LENGTH)
00947         {
00948           fprintf(stderr, "Memory Allocation error in creating table\n");
00949           abort();
00950         }
00951       }
00952       table_string.append(buf);
00953 
00954       if (col_count < num_char_cols || num_blob_cols > 0)
00955         table_string.append(",");
00956     }
00957 
00958   if (num_blob_cols)
00959     for (col_count= 1; col_count <= num_blob_cols; col_count++)
00960     {
00961       if (snprintf(buf, HUGE_STRING_LENGTH, "blobcol%d blob",
00962                    col_count) > HUGE_STRING_LENGTH)
00963       {
00964         fprintf(stderr, "Memory Allocation error in creating table\n");
00965         abort();
00966       }
00967       table_string.append(buf);
00968 
00969       if (col_count < num_blob_cols)
00970         table_string.append(",");
00971     }
00972 
00973   table_string.append(")");
00974   ptr= new Statement;
00975   ptr->setString(table_string.length());
00976   if (ptr->getString()==NULL)
00977   {
00978     fprintf(stderr, "Memory Allocation error in creating table\n");
00979     abort();
00980   }
00981   ptr->setType(CREATE_TABLE_TYPE);
00982   strcpy(ptr->getString(), table_string.c_str());
00983   return(ptr);
00984 }
00985 
00986 /*
00987   build_update_string()
00988 
00989   This function builds insert statements when the user opts to not supply
00990   an insert file or string containing insert data
00991 */
00992 static Statement *
00993 build_update_string(void)
00994 {
00995   char       buf[HUGE_STRING_LENGTH];
00996   uint32_t        col_count;
00997   Statement *ptr;
00998   string update_string;
00999 
01000   update_string.reserve(HUGE_STRING_LENGTH);
01001 
01002   update_string= "UPDATE t1 SET ";
01003 
01004   if (num_int_cols)
01005     for (col_count= 1; col_count <= num_int_cols; col_count++)
01006     {
01007       if (snprintf(buf, HUGE_STRING_LENGTH, "intcol%d = %ld", col_count,
01008                    random()) > HUGE_STRING_LENGTH)
01009       {
01010         fprintf(stderr, "Memory Allocation error in creating update\n");
01011         abort();
01012       }
01013       update_string.append(buf);
01014 
01015       if (col_count < num_int_cols || num_char_cols > 0)
01016         update_string.append(",", 1);
01017     }
01018 
01019   if (num_char_cols)
01020     for (col_count= 1; col_count <= num_char_cols; col_count++)
01021     {
01022       char rand_buffer[RAND_STRING_SIZE];
01023       int buf_len= get_random_string(rand_buffer, RAND_STRING_SIZE);
01024 
01025       if (snprintf(buf, HUGE_STRING_LENGTH, "charcol%d = '%.*s'", col_count,
01026                    buf_len, rand_buffer)
01027           > HUGE_STRING_LENGTH)
01028       {
01029         fprintf(stderr, "Memory Allocation error in creating update\n");
01030         abort();
01031       }
01032       update_string.append(buf);
01033 
01034       if (col_count < num_char_cols)
01035         update_string.append(",", 1);
01036     }
01037 
01038   if (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary)
01039     update_string.append(" WHERE id = ");
01040 
01041 
01042   ptr= new Statement;
01043 
01044   ptr->setString(update_string.length());
01045   if (ptr->getString() == NULL)
01046   {
01047     fprintf(stderr, "Memory Allocation error in creating update\n");
01048     abort();
01049   }
01050   if (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary)
01051     ptr->setType(UPDATE_TYPE_REQUIRES_PREFIX);
01052   else
01053     ptr->setType(UPDATE_TYPE);
01054   strncpy(ptr->getString(), update_string.c_str(), ptr->getLength());
01055   return(ptr);
01056 }
01057 
01058 
01059 /*
01060   build_insert_string()
01061 
01062   This function builds insert statements when the user opts to not supply
01063   an insert file or string containing insert data
01064 */
01065 static Statement *
01066 build_insert_string(void)
01067 {
01068   char       buf[HUGE_STRING_LENGTH];
01069   uint32_t        col_count;
01070   Statement *ptr;
01071   string insert_string;
01072 
01073   insert_string.reserve(HUGE_STRING_LENGTH);
01074 
01075   insert_string= "INSERT INTO t1 VALUES (";
01076 
01077   if (auto_generate_sql_autoincrement)
01078   {
01079     insert_string.append("NULL");
01080 
01081     if (num_int_cols || num_char_cols)
01082       insert_string.append(",");
01083   }
01084 
01085   if (auto_generate_sql_guid_primary)
01086   {
01087     insert_string.append("uuid()");
01088 
01089     if (num_int_cols || num_char_cols)
01090       insert_string.append(",");
01091   }
01092 
01093   if (auto_generate_sql_secondary_indexes)
01094   {
01095     uint32_t count;
01096 
01097     for (count= 0; count < auto_generate_sql_secondary_indexes; count++)
01098     {
01099       if (count) /* Except for the first pass we add a comma */
01100         insert_string.append(",");
01101 
01102       insert_string.append("uuid()");
01103     }
01104 
01105     if (num_int_cols || num_char_cols)
01106       insert_string.append(",");
01107   }
01108 
01109   if (num_int_cols)
01110     for (col_count= 1; col_count <= num_int_cols; col_count++)
01111     {
01112       if (snprintf(buf, HUGE_STRING_LENGTH, "%ld", random()) > HUGE_STRING_LENGTH)
01113       {
01114         fprintf(stderr, "Memory Allocation error in creating insert\n");
01115         abort();
01116       }
01117       insert_string.append(buf);
01118 
01119       if (col_count < num_int_cols || num_char_cols > 0)
01120         insert_string.append(",");
01121     }
01122 
01123   if (num_char_cols)
01124     for (col_count= 1; col_count <= num_char_cols; col_count++)
01125     {
01126       int buf_len= get_random_string(buf, RAND_STRING_SIZE);
01127       insert_string.append("'", 1);
01128       insert_string.append(buf, buf_len);
01129       insert_string.append("'", 1);
01130 
01131       if (col_count < num_char_cols || num_blob_cols > 0)
01132         insert_string.append(",", 1);
01133     }
01134 
01135   if (num_blob_cols)
01136   {
01137     vector <char> blob_ptr;
01138 
01139     blob_ptr.resize(num_blob_cols_size);
01140 
01141     for (col_count= 1; col_count <= num_blob_cols; col_count++)
01142     {
01143       uint32_t buf_len;
01144       uint32_t size;
01145       uint32_t difference= num_blob_cols_size - num_blob_cols_size_min;
01146 
01147       size= difference ? (num_blob_cols_size_min + (random() % difference)) :
01148         num_blob_cols_size;
01149 
01150       buf_len= get_random_string(&blob_ptr[0], size);
01151 
01152       insert_string.append("'", 1);
01153       insert_string.append(&blob_ptr[0], buf_len);
01154       insert_string.append("'", 1);
01155 
01156       if (col_count < num_blob_cols)
01157         insert_string.append(",", 1);
01158     }
01159   }
01160 
01161   insert_string.append(")", 1);
01162 
01163   ptr= new Statement;
01164   ptr->setString(insert_string.length());
01165   if (ptr->getString()==NULL)
01166   {
01167     fprintf(stderr, "Memory Allocation error in creating select\n");
01168     abort();
01169   }
01170   ptr->setType(INSERT_TYPE);
01171   strcpy(ptr->getString(), insert_string.c_str());
01172   return(ptr);
01173 }
01174 
01175 
01176 /*
01177   build_select_string()
01178 
01179   This function builds a query if the user opts to not supply a query
01180   statement or file containing a query statement
01181 */
01182 static Statement *
01183 build_select_string(bool key)
01184 {
01185   char       buf[HUGE_STRING_LENGTH];
01186   uint32_t        col_count;
01187   Statement *ptr;
01188   string query_string;
01189 
01190   query_string.reserve(HUGE_STRING_LENGTH);
01191 
01192   query_string.append("SELECT ", 7);
01193   if (not auto_generate_selected_columns_opt.empty())
01194   {
01195     query_string.append(auto_generate_selected_columns_opt.c_str());
01196   }
01197   else
01198   {
01199     for (col_count= 1; col_count <= num_int_cols; col_count++)
01200     {
01201       if (snprintf(buf, HUGE_STRING_LENGTH, "intcol%d", col_count)
01202           > HUGE_STRING_LENGTH)
01203       {
01204         fprintf(stderr, "Memory Allocation error in creating select\n");
01205         abort();
01206       }
01207       query_string.append(buf);
01208 
01209       if (col_count < num_int_cols || num_char_cols > 0)
01210         query_string.append(",", 1);
01211 
01212     }
01213     for (col_count= 1; col_count <= num_char_cols; col_count++)
01214     {
01215       if (snprintf(buf, HUGE_STRING_LENGTH, "charcol%d", col_count)
01216           > HUGE_STRING_LENGTH)
01217       {
01218         fprintf(stderr, "Memory Allocation error in creating select\n");
01219         abort();
01220       }
01221       query_string.append(buf);
01222 
01223       if (col_count < num_char_cols || num_blob_cols > 0)
01224         query_string.append(",", 1);
01225 
01226     }
01227     for (col_count= 1; col_count <= num_blob_cols; col_count++)
01228     {
01229       if (snprintf(buf, HUGE_STRING_LENGTH, "blobcol%d", col_count)
01230           > HUGE_STRING_LENGTH)
01231       {
01232         fprintf(stderr, "Memory Allocation error in creating select\n");
01233         abort();
01234       }
01235       query_string.append(buf);
01236 
01237       if (col_count < num_blob_cols)
01238         query_string.append(",", 1);
01239     }
01240   }
01241   query_string.append(" FROM t1");
01242 
01243   if ((key) &&
01244       (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary))
01245     query_string.append(" WHERE id = ");
01246 
01247   ptr= new Statement;
01248   ptr->setString(query_string.length());
01249   if (ptr->getString() == NULL)
01250   {
01251     fprintf(stderr, "Memory Allocation error in creating select\n");
01252     abort();
01253   }
01254   if ((key) &&
01255       (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary))
01256     ptr->setType(SELECT_TYPE_REQUIRES_PREFIX);
01257   else
01258     ptr->setType(SELECT_TYPE);
01259   strcpy(ptr->getString(), query_string.c_str());
01260   return(ptr);
01261 }
01262 
01263 static int
01264 process_options(void)
01265 {
01266   struct stat sbuf;
01267   OptionString *sql_type;
01268   uint32_t sql_type_count= 0;
01269   ssize_t bytes_read= 0;
01270   
01271   if (user.empty())
01272     user= "root";
01273 
01274   verbose= opt_verbose.length();
01275 
01276   /* If something is created we clean it up, otherwise we leave schemas alone */
01277   if ( (not create_string.empty()) || auto_generate_sql)
01278     opt_preserve= false;
01279 
01280   if (auto_generate_sql && (not create_string.empty() || !user_supplied_query.empty()))
01281   {
01282     fprintf(stderr,
01283             "%s: Can't use --auto-generate-sql when create and query strings are specified!\n",
01284             SLAP_NAME);
01285     abort();
01286   }
01287 
01288   if (auto_generate_sql && auto_generate_sql_guid_primary &&
01289       auto_generate_sql_autoincrement)
01290   {
01291     fprintf(stderr,
01292             "%s: Either auto-generate-sql-guid-primary or auto-generate-sql-add-autoincrement can be used!\n",
01293             SLAP_NAME);
01294     abort();
01295   }
01296 
01297   if (auto_generate_sql && num_of_query && auto_actual_queries)
01298   {
01299     fprintf(stderr,
01300             "%s: Either auto-generate-sql-execute-number or number-of-queries can be used!\n",
01301             SLAP_NAME);
01302     abort();
01303   }
01304 
01305   parse_comma(not concurrency_str.empty() ? concurrency_str.c_str() : "1", concurrency);
01306 
01307   if (not opt_csv_str.empty())
01308   {
01309     opt_silent= true;
01310 
01311     if (opt_csv_str[0] == '-')
01312     {
01313       csv_file= fileno(stdout);
01314     }
01315     else
01316     {
01317       if ((csv_file= open(opt_csv_str.c_str(), O_CREAT|O_WRONLY|O_APPEND, 
01318                           S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1)
01319       {
01320         fprintf(stderr,"%s: Could not open csv file: %sn\n",
01321                 SLAP_NAME, opt_csv_str.c_str());
01322         abort();
01323       }
01324     }
01325   }
01326 
01327   if (opt_only_print)
01328     opt_silent= true;
01329 
01330   if (not num_int_cols_opt.empty())
01331   {
01332     OptionString *str;
01333     parse_option(num_int_cols_opt.c_str(), &str, ',');
01334     num_int_cols= atoi(str->getString());
01335     if (str->getOption())
01336       num_int_cols_index= atoi(str->getOption());
01337     option_cleanup(str);
01338   }
01339 
01340   if (not num_char_cols_opt.empty())
01341   {
01342     OptionString *str;
01343     parse_option(num_char_cols_opt.c_str(), &str, ',');
01344     num_char_cols= atoi(str->getString());
01345     if (str->getOption())
01346       num_char_cols_index= atoi(str->getOption());
01347     else
01348       num_char_cols_index= 0;
01349     option_cleanup(str);
01350   }
01351 
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     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 void slap_connect(drizzle_con_st &con, 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   if ((drizzle= drizzle_create(NULL)) == NULL ||
02398       drizzle_con_add_tcp(drizzle, &con, host.c_str(), opt_drizzle_port,
02399         user.c_str(),
02400         opt_password.c_str(),
02401         connect_to_schema ? create_schema_string.c_str() : NULL,
02402         use_drizzle_protocol ? DRIZZLE_CON_EXPERIMENTAL : DRIZZLE_CON_MYSQL) == NULL)
02403   {
02404     fprintf(stderr,"%s: Error creating drizzle object\n", SLAP_NAME);
02405     abort();
02406   }
02407 
02408   drizzle_set_context(drizzle, (void*)(connection_count.fetch_and_increment()));
02409 
02410   if (opt_only_print)
02411     return;
02412 
02413   for (uint32_t x= 0; x < 10; x++)
02414   {
02415     if ((ret= drizzle_con_connect(&con)) == DRIZZLE_RETURN_OK)
02416     {
02417       /* Connect suceeded */
02418       connect_error= 0;
02419       break;
02420     }
02421     usleep(connection_retry_sleep);
02422   }
02423   if (connect_error)
02424   {
02425     fprintf(stderr,"%s: Error when connecting to server: %d %s\n", SLAP_NAME,
02426             ret, drizzle_con_error(&con));
02427     abort();
02428   }
02429 }
02430 
02431 void standard_deviation(Conclusions &con, Stats *sptr)
02432 {
02433   long int sum_of_squares;
02434   double the_catch;
02435   Stats *ptr;
02436 
02437   if (iterations == 1 || iterations == 0)
02438   {
02439     con.setStdDev(0);
02440     return;
02441   }
02442 
02443   uint32_t x;
02444   for (ptr= sptr, x= 0, sum_of_squares= 0; x < iterations; ptr++, x++)
02445   {
02446     long int deviation;
02447 
02448     deviation= ptr->getTiming() - con.getAvgTiming();
02449     sum_of_squares+= deviation*deviation;
02450   }
02451 
02452   the_catch= sqrt((double)(sum_of_squares/(iterations -1)));
02453   con.setStdDev((long int)the_catch);
02454 }