sillyql / functions.cpp
functions.cpp
Raw
// Project Identifier: C0F4DFE8B340D81183C208F70F9D2D797908754D
#include <queue>
#include <stack>
#include <deque>
#include <list>
#include <getopt.h>
#include <sstream>
#include <unistd.h>
#include <iostream>
#include <string>
#include <vector>
#include <stdio.h>
#include <ctype.h>
#include <algorithm>
#include <map>
#include <unordered_map>
#include "functions.h"

using namespace std;

// COMMAND LINE ////////////////////////////////////////////////////
void process_cmd_line(int argc, char** argv, Silly &silly){
    //getopt
    int gotopt;
    int option_index = 0;
    static struct option long_opts[] = {
        {"help", no_argument, nullptr, 'h'},
        {"quiet", no_argument, nullptr, 'q'},
        {NULL, 0, NULL, 0}
    };
    while((gotopt = getopt_long(argc, argv, "hq", 
            long_opts, &option_index)) != -1){
        switch(gotopt){
            case 'h':
                std::cout << "You asked for help!\n";
                exit(0);
                break;
            case 'q':
                silly.quiet = true;
                break;
            default:
            abort();
        }
    }
}

// UTILITY ////////////////////////////////////////////////////
// set command
char set_command(string &cmd){
    string junk;
    // COMMENT
    if(cmd[0] == '#'){
        return 'y';
    }
    // CREATE
    else if(cmd == "CREATE"){
        return 'c';
    }
    // QUIT
    else if(cmd == "QUIT"){
        return 'q';
    }
    // REMOVE
    else if(cmd == "REMOVE"){
        return 'r';
    }
    // INSERT
    else if(cmd == "INSERT"){
        //get INTO
        cin >> junk;
        return 'i';
    }
    // DELETE
    else if(cmd == "DELETE"){
        //get FROM
        cin >> junk;
        return 'd';
    }
    // PRINT
    else if(cmd == "PRINT"){
        //get FROM
        cin >> junk;
        return 'p';
    }
    // JOIN
    else if(cmd == "JOIN"){
        return 'j';
    }
    // GENERATE
    else if(cmd == "GENERATE"){
        //get FOR
        cin >> junk;
        return 'g';
    }
    return 'z';
}

// create entry based on coltype
TableEntry create_entry(Column &column){
    // double
    if(column.coltype == 'd'){
        double argument;
        cin >> argument;
        return TableEntry(argument);
    }
    // int
    else if(column.coltype == 'i'){
        int argument;
        cin >> argument;
        return TableEntry(argument);
    }
    // bool
    else if(column.coltype == 'b'){
        bool argument;
        cin >> argument;
        return TableEntry(argument);
    }
    // string
    else if(column.coltype == 's'){
        string argument;
        cin >> argument;
        return TableEntry(argument);
    }
    else {
        return TableEntry(0);
    }
}

// if column in table, returns column index. else, returns -1
size_t get_col_index(string &colname, Table &table){
    for(size_t i=0; i < table.cols.size(); i++){
        // if column in table, return true
        if(table.cols[i].colname == colname){
            return i;
        }
    }
    return static_cast<size_t>(-1);
}

// FUNCTIONS //////////////////////////////////////////////////////////////
// CREATE
void create_table(string &argument, unordered_map<string, Table> &table_map){
    std::cin >> argument;
    // ERROR CHECK: for if table already exists
    if(check_table_exists(argument, table_map)){
        // print already exists
        print_table_exists(argument, "CREATE");
        cin.clear();
        return;
    }
            
    Table new_table(argument, 0);
    argument = "";
    // in num columns
    cin >> argument;
    new_table.num_cols = stoi(argument);
    argument = "";
    // in coltypes
    int name_count = new_table.num_cols;
    while(name_count > 0){
        cin >> argument;
        Column column(argument[0]);
        new_table.cols.emplace_back(column);
        argument = "";
        name_count--;
                }
        //in colnames
        for(size_t i=0; i< new_table.cols.size(); i++){
            cin >> argument;
            new_table.cols[i].colname = argument;
            argument = "";
        }

    // insert table into map
    std::pair<string, Table> insert_table (new_table.tablename, new_table);
    table_map.insert(insert_table);
    
    //ouput
    std::cout << "New table " << new_table.tablename << " with column(s) ";
        for(size_t i=0; i < new_table.cols.size(); i++){
            std::cout << new_table.cols[i].colname << " ";
            }
    std::cout << "created\n";
    }

// INSERT
void insert_into(string &argument, unordered_map<string, Table> &table_map){
    // table_insert(tablename, table_map);
    // in tablename
    cin >> argument;
    string tablename = argument;
    argument = "";

    // in num rows
    cin >> argument;
    int num_rows = stoi(argument);

    // in junk "ROWS"
    cin >> argument;
    argument = "";

    // ERROR CHECK: if table does not exist
    if(!check_table_exists(tablename, table_map)){
        // print does not exist
        print_table_n_exists(tablename, "INSERT");
        cin.clear();
    }
    else {
        auto it = table_map.find(tablename);
        // set start
        size_t start = it->second.cols[0].col.size();
        // for each row
        for(int i=0; i < num_rows; i++){
            // for each column
            for(size_t j=0; j < it->second.cols.size(); j++ ){
                // check column type, create entry and push into column
                it->second.cols[j].col.emplace_back(create_entry(it->second.cols[j]));
            }
        }
    // set end
    size_t end = it->second.cols[0].col.size();
    end--;
    std::cout << "Added " << num_rows << " rows to " << tablename << " from position " << start << " to " << end << "\n"; 
    }
}

// takes coltype, operation and value, adjusts value based on coltype, and returns true if row should be deleted
bool delete_check(char &coltype, TableEntry &col_entry, string &operation, string &value){
    // if coltype is bool
    if(coltype == 'b'){
        bool value_bool = false;
        if(value == "true"){
            value_bool = true;
        }
        if(operation == "="){
            return col_entry == value_bool;
        }
        else if(operation == ">"){
            return col_entry > value_bool;
        }
        // less than
        else {
            return col_entry < value_bool;
        }
    }

    // if coltype is double
    else if(coltype == 'd'){
        double value_double = stod(value);
        if(operation == "="){
            return col_entry == value_double;
        }
        else if(operation == ">"){
            return col_entry > value_double;
        }
        // less than
        else {
            return col_entry < value_double;
        }
    }

    // if coltype is int
    else if(coltype == 'i'){
        int value_int = stoi(value);
        if(operation == "="){
            return col_entry == value_int;
        }
        else if(operation == ">"){
            return col_entry > value_int;
        }
        // less than
        else {
            return col_entry < value_int;
        }
    }
    // if coltype is string
    else{
        if(operation == "="){
            return col_entry == value;
        }
        else if(operation == ">"){
            return col_entry > value;
        }
        // less than
        else{
            return col_entry < value;
        }
    }
    return false;
}

// for each column in table, delete entry at col_index
void execute_delete(Table &table, size_t col_index){
    // vector<TableEntry>::iterator it;
    int delete_index= 0;
    for(size_t i=0; i < col_index; i++){
        delete_index++;
    }
    // for each column in table, remove element at col index
    for(size_t i=0; i < table.cols.size(); i++){
        // set it to item to be deleted
        auto it = table.cols[i].col.begin() + delete_index;
        vector<TableEntry> split;
        it++;

        // add to split after delete point
        while(it != table.cols[i].col.end()){
            split.emplace_back(*it);
            it++;
        }

        // clear out original
        for(size_t j=0; j < split.size(); j++){
            table.cols[i].col.pop_back();
        }
       
        // delete
        table.cols[i].col.pop_back();
        
        // add back to original
        // std::vector<TableEntry>::reverse_iterator iter = split.rbegin();
        for(size_t k=0; k < split.size(); k++){
            table.cols[i].col.emplace_back(split[k]);
        }
        while(!split.empty()){
            split.pop_back();
        }
    }
}

//delete rows
void delete_rows(string &argument, unordered_map<std::string, Table> &table_map){
    // get tablename
    string tablename;
    cin >> argument;
    tablename = argument;
    argument = "";

    // cin WHERE
    cin >> argument;
    argument = "";

    // get colname
    string colname;
    cin >> colname;

    // get operation
    string operation;
    cin >> operation;

    // get value
    cin >> argument;
    
    // ERROR CHECK: if table does not exist
    if(!check_table_exists(tablename, table_map)){
        // print does not exist
        print_table_n_exists(tablename, "DELETE");
        cin.clear();
        return;
    }

    auto it = table_map.find(tablename);
    
    // ERROR CHECK: if column does not exist
    if(!check_column(colname, it->second)){
        // print does not exist
        print_check_column(colname, "DELETE", it->second);
        cin.clear();
        return;
    }

            
    // adjust value
    for(size_t i=0; i<it->second.cols.size(); i++){
        if(it->second.cols[i].colname == colname){
            string delete_coltype;
            delete_coltype = it->second.cols[i].coltype;
        }
    }

    // value will always be same type as coltype
    int delete_count = 0;
    //set search column to colname
    for(size_t i=0; i < it->second.cols.size(); i++){
        if(it->second.cols[i].colname == colname){
            //if column entry with op == true, delete row
            for(size_t j = 0; j < it->second.cols[i].col.size(); j++){
                //if delete check returns true, delete entry index for each column in table
                if(delete_check(it->second.cols[i].coltype, it->second.cols[i].col[j], operation, argument) == true){
                    execute_delete(it->second, j);
                    delete_count++;
                    }
                } 
            }
        }
    std::cout << "Deleted " << delete_count << " rows from " << tablename << "\n";
}

// generate index
void generate_index(string &colname, unordered_map<std::string, Table> &table_map){
    string tablename;
    cin >> tablename;
    string indextype;
    cin >> indextype;
    // junk
    cin >> colname;
    cin >> colname;
    colname = "";
    // get colname
    cin >> colname;

    // set table it
    // ERROR: table does not exist
    if(!check_table_exists(tablename, table_map)){
        print_table_n_exists(tablename, "GENERATE");
        cin.clear();
        return;
    }
    auto it = table_map.find(tablename);

    // set column index
    // ERROR: colname not in table
    if(!check_column(colname, it->second)){
        print_check_column(colname, "GENERATE", it->second);
        cin.clear();
        return;
    }
    size_t index = get_col_index(colname, it->second);

    // clear if current index exists
    if(!it->second.cols[index].hash_map.empty()){
        it->second.cols[index].hash_map.clear();
        }
    if(!it->second.cols[index].bst_map.empty()){
        it->second.cols[index].bst_map.clear();
        }

    // HASH
    // keep track of insertion order into table
    
    // given column value in colname, row index is easy to find
    // key is column value, value is row index
    // iterate through column
    // create table entry, row index pair
    // insert to hash map
    if(indextype == "hash"){
        // set booleans
        it->second.cols[index].hash_index = true;
        it->second.cols[index].bst_index = false;
        // iterate through given column
        for(size_t i=0; i < it->second.cols[index].col.size(); i++){
            // i = row index
            // create + insert key (entry) value (row) pair
            it->second.cols[index].hash_map.insert(make_pair(it->second.cols[index].col[i], i));
            }
        }

    // BST
    if(indextype == "bst"){
        // set booleans
        it->second.cols[index].bst_index = true;
        it->second.cols[index].hash_index = false;
        // iterate through given column
        for(size_t i=0; i < it->second.cols[index].col.size(); i++){
            // i = row index
            // create + insert key (entry) value (row) pair
            it->second.cols[index].bst_map.insert(make_pair(it->second.cols[index].col[i], i));
            }
        }
    std::cout << "Created " << indextype << " index for table " << tablename << " on column " << colname << "\n";
}

// takes coltype, operation and value, adjusts value based on coltype, and returns true if row should be printed
bool print_check(Table &table_map, size_t &index, string &colname, string &operation, string &value){
    // for each column in table
    for(size_t i=0; i < table_map.cols.size(); i++){
        // find op column
        if(table_map.cols[i].colname == colname){
            // if coltype is bool
            if(table_map.cols[i].coltype == 'b'){
                bool value_bool = false;
                if(value == "true"){
                    value_bool = true;
                }
                if(operation == "="){
                    return table_map.cols[i].col[index] == value_bool;
                }
                else if(operation == ">"){
                    return table_map.cols[i].col[index] > value_bool;
                }
                // less than
                else {
                    return table_map.cols[i].col[index] < value_bool;
                }
            }

            // if coltype is double
            else if(table_map.cols[i].coltype == 'd'){
                double value_double = stod(value);
                if(operation == "="){
                    return table_map.cols[i].col[index] == value_double;
                }
                else if(operation == ">"){
                    return table_map.cols[i].col[index] > value_double;
                }
                // less than
                else {
                    return table_map.cols[i].col[index] < value_double;
                }
            }

            // if coltype is int
            else if(table_map.cols[i].coltype == 'i'){
                int value_int = stoi(value);
                if(operation == "="){
                    return table_map.cols[i].col[index] == value_int;
                }
                else if(operation == ">"){
                    return table_map.cols[i].col[index] > value_int;
                }
                // less than
                else {
                    return table_map.cols[i].col[index] < value_int;
                }
            }
            // if coltype is string
            else{
                if(operation == "="){
                    return table_map.cols[i].col[index] == value;
                }
                else if(operation == ">"){
                    return table_map.cols[i].col[index] > value;
                }
                // less than
                else{
                    return table_map.cols[i].col[index] < value;
                }
            }
        }
        
    }
    //colname not found
    return false;
}

// print
void print_rows(string &argument, unordered_map<std::string, Table> &table_map, Silly &silly){
    // get table name
    string tablename;
    cin >> argument;
    tablename = argument;
    argument = "";
    // get col number
    int col_num;
    cin >> argument;
    col_num = stoi(argument);
    argument = "";
   
    size_t row_count = 0;
    vector<string> cols_to_print;
    while(col_num > 0){
        cin >> argument;
        cols_to_print.push_back(argument);
        col_num--;
    }

    // get argument
    argument = "";
    cin >> argument;

    if(!silly.quiet){
        // print column names
        for(size_t i=0; i<cols_to_print.size(); i++){
            std::cout << cols_to_print[i] << " ";
        }
        std::cout << "\n";
    }


    // ALL
    if(argument == "ALL"){
        // set it to table
        auto it = table_map.find(tablename);
        // for length of column
        for(size_t i=0; i < it->second.cols[0].col.size(); i++){
            // iterate through columns to print
            for(size_t k=0; k < cols_to_print.size(); k++){
                // ERROR: col_to_print not in table
                if(!check_column(cols_to_print[k], it->second)){
                    print_check_column(cols_to_print[k], "PRINT", it->second);
                    cin.clear();
                    return;
                }
                size_t print_index = get_col_index(cols_to_print[k], it->second);
                // !QUIET
                if(!silly.quiet){
                    // PRINTING
                    std::cout << it->second.cols[print_index].col[i] << " ";
                }
            }
        row_count++;
        std::cout << "\n";
        }
    }
    // WHERE
    if(argument == "WHERE"){
        string colname = "";
        string operation = "";
        string condition;
        cin >> colname >> operation >> condition;

        auto it = table_map.find(tablename);
        // for column length
        for(size_t i=0; i < it->second.cols[0].col.size(); i++){
            // check if row should be skipped
            if(print_check(it->second, i, colname, operation, condition)){
                // iterate through cols to print
                for(size_t k=0; k < cols_to_print.size(); k++){
                    // ERROR: col_to_print not in table
                    if(!check_column(cols_to_print[k], it->second)){
                        print_check_column(cols_to_print[k], "PRINT", it->second);
                        cin.clear();
                        return;
                    }
                    // get column to print
                    size_t print_index = get_col_index(cols_to_print[k], it->second);
                    // !QUIET
                    if(!silly.quiet){
                        // PRINTING
                        // if BST
                        /*
                        if(it->second.cols[print_index].bst_index == true){
                            //variable i needs to be different
                            // create map iterator
                            map<TableEntry, size_t>::iterator bst_it;
                            // set iterator to beginning of map
                            bst_it = it->second.cols[print_index].bst_map.begin();
                            size_t increment = 0;
                            while(increment != i){
                                ++increment;
                                ++bst_it;
                                std::cout << bst_it->first << " ";
                            }
                        }
                        */
                        //else{
                            std::cout << it->second.cols[print_index].col[i] << " ";
                        //}
                    }
                }
            }
            else {
                continue;
            }
        row_count++;
        std::cout << "\n";
        }
    }
   std::cout << "Printed " << row_count << " matching rows from " << tablename << "\n";
}

// print join row
// print each column in row
void print_j_row(Table &table1, Table &table2, vector<string> &colnames, vector<int> &table_specs, size_t &index_one, size_t &index_two){
    // for colnames container
    for(size_t i=0; i < colnames.size(); i++){
        // if column is from table 1
        if(table_specs[i] == 1){
            // set col index in table 1
            size_t col_index = get_col_index(colnames[i], table1);
            // print item in column at row index
            std::cout << table1.cols[col_index].col[index_one] << " ";
        }
        // table_specs[i] is 2
        else {
            // set col index in table 2
            size_t col_index = get_col_index(colnames[i], table2);
            // print item in column at row index
            std::cout << table2.cols[col_index].col[index_two] << " ";
        }
    } 
    std::cout << "\n";
}

// JOIN
// print rows where col1 matches col2
void join_tables(string &argument, unordered_map<string, Table> &table_map, Silly &silly){
    // join tables where values in selected columns match
    // INPUT
    string tablename_one;
    string tablename_two;
    string colname_one;
    string colname_two;
    string order;

    // order in printing
    vector<int> table_specs;
    vector<string> colnames;

    size_t count;
    string junk;

    // get t1
    cin >> tablename_one;
    // AND
    cin >> junk;
    // get t2
    cin >> tablename_two;
    // WHERE
    cin >> junk;

    cin >>colname_one;
    // =
    cin >> junk;

    // get colname two
    cin >> colname_two;
    // AND PRINT
    cin >> junk;
    cin >> junk;

    // get rowcount
    cin >> argument;
    count = static_cast<size_t>(stoi(argument));
    argument = "";

    // get output specs
    size_t cin_count = 0;
    while(cin_count != count){
        // get column name
        cin >> argument;
        colnames.push_back(argument);
        argument = "";
        // get table
        cin >> argument;
        table_specs.push_back(stoi(argument));
        cin_count++;
    }

    if(!silly.quiet){
        // output colnames
        for(size_t i=0; i < colnames.size(); i++){
            std::cout << colnames[i] << " ";
        }
        std::cout << "\n";
    }

    // OUTPUT TABLE INFO
    // error check tables
    // ERROR: tablename_one does not exist
    if(!check_table_exists(tablename_one, table_map)){
        print_table_n_exists(tablename_one, "JOIN");
        cin.clear();
        return;
    }
    // ERROR: tablename_two does not exist
    if(!check_table_exists(tablename_two, table_map)){
        print_table_n_exists(tablename_two, "JOIN");
        cin.clear();
        return;
    }

    // assign table its
    auto it1 = table_map.find(tablename_one);
    auto it2 = table_map.find(tablename_two);

    // GET COLUMNS
    // init column indexes
    size_t index_one = 0;
    size_t index_two = 0;

    // ERROR: colname_one not in table_one  
    if(!check_column(colname_one, it1->second)){
        print_check_column(colname_one, "JOIN", it1->second);
        cin.clear();
        return;
        
    }
    // ERROR: colname_two not in table_two
    if(!check_column(colname_two, it2->second)){
        print_check_column(colname_two, "JOIN", it2->second);
        cin.clear();
        return;
    }

    index_one = get_col_index(colname_one, it1->second);
    index_two = get_col_index(colname_two, it2->second);

    // for the length of colname one
    for(size_t i=0; i < it1->second.cols[index_one].col.size(); i++){
        // for the length of colname two
        for(size_t j=0; j < it2->second.cols[index_two].col.size(); j++){
            // if value in col one is equal to value in col 2
            if(it1->second.cols[index_one].col[i] == it2->second.cols[index_two].col[j]){
                //std:: cout << it1->second.cols[index_one].col[j] << " and " << it2->second.cols[index_two].col[k] << " are equal.\n";
                // print row
                if(!silly.quiet){
                    print_j_row(it1->second, it2->second, colnames, table_specs, i, j);
                    }  
                } 
            }
        }         
    std::cout << "Printed " << count << " rows from joining " << tablename_one << " to " << tablename_two << "\n";
}

// REMOVE
void remove_table(string &argument, unordered_map<string, Table> &table_map){
    cin >> argument;
    // ERROR CHECK: if table doesn't exist
    if(!check_table_exists(argument, table_map)){
        print_table_n_exists(argument, "REMOVE");
        cin.clear();
    }
    else {
        table_map.erase(argument);
        std::cout << "Table " << argument << " deleted\n";
    }
}

// ERRORS
// table already exists
bool check_table_exists(string &tablename, unordered_map<string, Table> &table_map){
    // if table already exists, return true
    if(table_map.find(tablename) == table_map.end()){
        return false;  
    }
    return true;
}
 // print already exists
void print_table_exists(string &tablename, string command){
    std::cout << "Error during " << command << ": Cannot create already existing table " << tablename << ".\n";  
}
 // print does not exist
void print_table_n_exists(string &tablename, string command){
    std::cout << "Error during " << command << ": Cannot create already existing table " << tablename << " does not name a table in the database\n";  
}

// returns true if column is in table
bool check_column(string &colname, Table &table){
    if(get_col_index(colname, table) == static_cast<size_t>(-1)){
        return false;
    }
    return true;
}
// print column not in table
void print_check_column(string &colname, string command, Table &table){
    std::cout << "Error during " << command << ": " << colname << " does not name a column in " << table.tablename << "\n";
}