/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include "../config.h"

#include <ctype.h>
#include <math.h>

#include "die.h"
#include "smack.h"
#include "engines.h"
#include "parse.h"

extern int yylineno;

map <string, Query*, less<string> > query_pool;
map <string, Dictionary*, less<string> > dict_pool;
map <string, Client*, less<string> > client_pool;  
map<string, Val*, less<string> > var_hash;
string current_client = "main";
vector<int> client_pids;

Query_def_line::Query_def_line(string& str):str(str)
{
}

void Query_name_def_line::set_q_param(Query* q)
{
  q->name = str;
}

void Query_query_def_line::set_q_param(Query* q)
{
  q->query = str;
}

void Query_type_def_line::set_q_param(Query* q)
{
  q->type_name = str;
}

void Query_has_result_set_def_line::set_q_param(Query* q)
{
  q->has_result_set = (tolower(str[0]) == 'y');
}

void Query_parsed_def_line::set_q_param(Query* q)
{
  q->parsed = (tolower(str[0]) == 'y');
}

Dictionary* Dict_def::make_dict()
{
  Dictionary* dict = NULL;
  
  if(dict_type == "rand")
    {
      dict = new Rand_dictionary;
    }
  else if(dict_type == "seq")
    {
      dict = new Seq_dictionary;
    }
  else if(dict_type == "unique")
    {
      dict = new Unique_dictionary;
    }
  else
    die(0, "unknown dictionary type '%s' on line %d", dict_type.c_str(),
        yylineno);

  if(file_size_equiv.length())
    dict->file_size_equiv = atoi(file_size_equiv.c_str());
  if(delim.length())
    dict->delim = delim[0];
  
  if(source_type == "file")
    {
      dict->load_file(source.c_str());
    }
  else if(source_type == "list")
    {
      dict->load_list(source.c_str());
    }
  else if(source_type == "template")
    {
      if(!(dict_type == "unique"))
	die(0, "line %d: template source type is supported only for \
dictionaries of type 'unique'", yylineno);
      ((Unique_dictionary*)dict)->set_template(source.c_str());
    }
  else
    die(0, "unknown dictionary source type: '%s' on line %d",
        source_type.c_str(), yylineno);
 
  dict->name = name;
  
  return dict;
}

void Dict_dict_type_def_line::update_dict_def(Dict_def& d)
{
  d.dict_type = str;
}

void Dict_source_type_def_line::update_dict_def(Dict_def& d)
{
  d.source_type = str;
}

void Dict_source_def_line::update_dict_def(Dict_def& d)
{
  d.source = str;
}

void Dict_delim_def_line::update_dict_def(Dict_def& d)
{
  d.delim = str;
}

void Dict_file_size_equiv_def_line::update_dict_def(Dict_def& d)
{
  d.file_size_equiv = str;
}

Table_def_line::Table_def_line(string& str):str(str)
{
}

void Table_def::validate_table(string& name)
{
  Client* c = client_pool[client];
  if(!c)
    die(0, "Missing or undefined client in the table section on line %d",
	yylineno);
  Table t;
  bool do_create = 0, do_drop = 0;
  int min_rows_lim = atoi(min_rows.c_str());
  c->connect();
  t.name = name;
  if(c->get_table_info(t))
    {
      do_create = 1;
    }
  else
    {
      if(t.num_rows < min_rows_lim)
	{
	  do_drop = 1;
	  do_create = 1;
	}
    }

  if(do_drop)
    {
      cout << "Table '" << name <<
	"' does not meet condtions, will be dropped" << endl;
      string query = "drop table ";
      query += name;
      c->safe_query(query.c_str());
    }
  
  if(do_create)
    {
      char path[MAX_PATH];
      const char* file = mk_data_path(path, data_file.c_str());
      struct stat f;
      cout << "Creating table '" << name << "'" << endl;
      c->safe_query(create_st.c_str());
      if(stat(file, &f) && populate_data_file(file, gen_data_file.c_str()))
	die(1, "Error generating data file");
      cout << "Loading data from file '" << file << "' into table '" << name <<
	   "'" << endl;
      c->load_table_data(name, file);
      cout << "Table " << name << " is now ready for the test" << endl;
    }

  c->disconnect();
}

void Table_client_def_line::update_table_def(Table_def* t)
{
  t->client = str;
}

void Table_create_def_line::update_table_def(Table_def* t)
{
  t->create_st = str;
}

void Table_min_rows_def_line::update_table_def(Table_def* t)
{
  t->min_rows = str;
}

void Table_data_file_def_line::update_table_def(Table_def* t)
{
  t->data_file = str;
}

void Table_gen_data_file_def_line::update_table_def(Table_def* t)
{
  t->gen_data_file = str;
}


Client* Client_def::make_client()
{
  Client* c;
  c = new_client(db_type);
  c->user = user;
  c->pass = pass;
  c->db = db;
  c->host = host;
  c->socket = this->socket;
  c->port = port;
  
  if(qb)
    c->set_query_barrel(qb);

  c->set_dictionary_pool(&dict_pool);
  
  return c;
}

void Client_query_barrel_def_line::update_client_def(Client_def& cd)
{
  int str_len = str.size();
  char buf[str_len + 1];
  int i,j = 0;
  int last_was_space = 1;
  int num_shots = 0;
  enum {NUM_SHOTS, QUERY_NAME,SLEEP_TIME} parse_state = NUM_SHOTS;
  cd.qb = new Query_barrel;
  
  for(i = 0; i < str_len; i++)
    {
     if(str[i] == ' ')
       if(last_was_space)
         continue;
       else
         {
           last_was_space = 1;
           buf[j] = 0;
           j = 0;
           
           switch(parse_state)
             {
             case NUM_SHOTS:
	       if(buf[0] == '-')
		 {
		   float sl = atof(buf+1);
		   int sec,usec;
		   sec = (int)floor(sl);
		   sl -= sec;
		   usec = (int)floor(sl*1000000);
		   cd.qb->add_sleep_action(sec,usec);
		   break;
		 }
               num_shots = atoi(buf);
               parse_state = QUERY_NAME;
               break;
               
             case QUERY_NAME:
               {
                 Query* q = query_pool[buf];
                 if(!q)
                   die(0, "query '%s' referenced on line %d has not \
been defined", buf, yylineno);
                 cd.qb->add_query_charge(*q, num_shots);
                 parse_state = NUM_SHOTS;
                 break;
               }
             }
         } // else
     else
       {
         last_was_space = 0;
         buf[j++] = str[i];
       }
       
    }

  if(!last_was_space)
    {
      buf[j] = 0;
      if(buf[0] == '-')
	{
	  float sl = atof(buf+1);
	  int sec,usec;
	  sec = (int)floor(sl);
	  sl -= sec;
	  usec = (int)floor(sl*1000000);
	  cd.qb->add_sleep_action(sec,usec);
	  return;
	}
      Query* q = query_pool[buf];
      if(!q)
        die(0, "query '%s' referenced on line %d has not been defined",
            buf, yylineno);
       cd.qb->add_query_charge(*q, num_shots);
    }
}

void Client_user_def_line::update_client_def(Client_def& cd)
{
  cd.user = str;
}

void Client_pass_def_line::update_client_def(Client_def& cd)
{
  cd.pass = str;
}

void Client_host_def_line::update_client_def(Client_def& cd)
{
  cd.host = str;
}

void Client_socket_def_line::update_client_def(Client_def& cd)
{
  cd.socket = str;
}

void Client_port_def_line::update_client_def(Client_def& cd)
{
  cd.port = str;
}


void Client_db_def_line::update_client_def(Client_def& cd)
{
  cd.db = str;
}

void Param_list::print()
{
  if(params.size() > 0)
    cout << params[0];
  
  for(int i = 1; i < params.size(); i++)
    {
      cout << "," << params[i];
    }
}

void Main_line::print()
{
  cout << "main_line: client: " << client << ",method=" << method ;
  if(param_list)
    {
      cout << ",param_list: " ;
      param_list->print();
    }

  cout << endl;
}

void Main_line::execute()
{
  if(!(client == current_client) && !(method == "init"))
    return;
  
  Client* c = client_pool[client];
  if(!c)
    die(0, "client '%s' referenced on line %d is not defined",
        client.c_str(), yylineno);
  if(method == "connect")
    {
      c->connect();
    }
  else if(method == "init")
    {
      if(!(current_client == "main")) return;
      pid_t pid;
      switch(pid = fork())
        {
        case 0:
          c->init();
          current_client = client;
          break;
        case -1:
          die(1, "could not fork()");
          break;
          
        default:
          client_pids.insert(client_pids.end(), pid);
          break;
        }
          
    }
  else if(method == "sleep")
    {
      if(param_list->size() != 2)
        die(0, "line %d: sleep takes two parameters (sec,ms), you have %d",
            yylineno, param_list->size());
      sleep(atoi(param_list->pchar_val(0)));
      usleep(atoi(param_list->pchar_val(1))/1000);
    }
  else if(method == "print")
    {
      if(param_list->size() != 1)
        die(0, "line %d: print takes one parameter, you have %d",
            yylineno, param_list->size());
      cout << param_list->pchar_val(0) << endl;
    }
  else if(method == "query")
    {
      if(param_list->size() != 1)
        die(0, "line %d: query takes one parameter, you have %d",
            yylineno, param_list->size());
      const char* q = param_list->pchar_val(0);
      c->safe_query(q);
    }
  else if(method == "run_save_result")
    {
      if(param_list->size() != 2)
        die(0, "line %d: run_save_result takes two parameters, you have %d",
            yylineno, param_list->size());
      const char* q_name = param_list->pchar_val(0);
      const char* fname = param_list->pchar_val(1);
      Query *q = query_pool[q_name];
      
      if(!q)
        die(0, "line %d: query '%s' has not been defined", yylineno,
            q_name);

      c->safe_query(*q);
      if(fname[0] == '-' && !fname[1])
	fname = NULL;
      if(q->has_result_set)
        c->dump_result(fname);
      
    }
  else if(method == "run_check_result")
    {
    }
  else if(method == "set_num_rounds")
    {
      if(param_list->size() != 1)
        die(0, "line %d: set_num_rounds takes one parameter, you have %d",
            yylineno, param_list->size());
      int num_rounds = param_list->int_val(0);
      c->set_num_rounds(num_rounds);
    }
  else if(method == "disconnect")
    {
      c->disconnect();
    }
  else if(method == "create_threads")
    {
      c->start_clock();
      if(param_list->size() != 1)
        die(0, "line %d: create_threads takes one parameter, you have %d",
            yylineno, param_list->size());
      int num_threads = param_list->int_val(0);
      c->create_threads(num_threads);
    }
  else if(method == "collect_threads")
    {
      c->thread_sync();
      c->stop_clock();
      cout << "Query Barrel Report for client " << client << endl;
      c->print_connect_times();
      c->print_barrel_report();
    }
  else if(method == "unload_query_barrel")
    {
      c->unload_query_barrel();
    }
  else
    {
      die(0, "unknown method '%s' called for client '%s' on line %d",
          method.c_str(), client.c_str(), yylineno);
    }
}








syntax highlighted by Code2HTML, v. 0.9.1