Relational-Database / relData.cpp
relData.cpp
Raw
#include <string>
#include <getopt.h>
#include <iostream>
#include "relData.h"
#include <algorithm>


using namespace std;


class Primary {
    private:
        bool quiet;
        unordered_map<string, Table> tables;

    public:
        void set_defaults() {
            quiet = false;
        }

        void help_option() {
            cout << "This program is intended to emulate a relational database." << endl;
            cout << "For more information, read README.txt file." << endl;
            exit(0);
        }

        void option_management(int argc, char *argv[])
        {
            string mode;
            opterr = false;
            int choice;
            int option_index = 0;
            // For information on getOpts and these statements below, read into the documentation of getOpts.h
            option longOpts[] = {
            { "quiet", no_argument, nullptr, 'q' },
            { "help", no_argument, nullptr, 'h'},
            { nullptr, 0, nullptr, '\0' }}; // Required closing bit

            while ((choice = getopt_long(argc, argv, "qh", longOpts, &option_index)) != -1) {
            switch (choice) {
                case 'q': {
                    quiet = true;
                    break;
                } // end case q
                case 'h': {
                    help_option();
                    break;
                } // end case h
                default:
                    cerr << "Unknown command line option" << endl;
                    exit(1);
                } // End of Switch
             } // End of While
            } // end of option_management()

        void SQL() 
        {
            string cmd;
            do {
                cout << "% ";
                cin >> cmd;
                char first = cmd.at(0);
                switch (first) {
                    case '#': {
                        string junk;
                        getline(cin, junk);
                        break;
                    } // end case '#'
                    case 'C': {
                        string name;
                        int number;
                        cin >> name >> number;
                        create_input_management(name, number);
                        break;
                    } // end case 'C'
                    case 'I': {
                        string inputName, junk;
                        int inputNumber;
                        cin >> junk >> inputName >> inputNumber >> junk;
                        input_input_management(inputName, inputNumber);
                        break;
                    } // end case 'I'
                    case 'D': {
                        string inputName, junk;
                        cin >> junk >> inputName >> junk;
                        string command = "DELETE";
                        bool tableLegit = table_exist_check(command, inputName);
                        if(!tableLegit) {
                            break;
                        }
                        delete_input_management(inputName);
                        break;
                    } // end case 'D'
                    case 'G': {
                        string junk, inputName, type, colName;
                        cin >> junk >> inputName >> type >> junk >> junk >> colName;
                        string command = "GENERATE";
                        bool validTable = table_exist_check(command, inputName);
                        if(!validTable) {
                            break;
                        }
                        bool validCol = column_exist_check(command, colName, inputName);
                        if(!validCol) {
                            break;
                        }
                        generate_index_management(inputName, type, colName);
                        break;
                    } // end case 'G'
                    case 'P': {
                        string junk, inputname;
                        int numCols;
                        cin >> junk >> inputname >> numCols;
                        string command = "PRINT";
                        bool tableValid = table_exist_check(command, inputname);
                        if(!tableValid) {
                            break;
                        }
                        print_input_management(inputname, numCols);
                        break;
                    } // end case 'P'
                    case 'J': {
                        join_input_management();
                        break;
                    } // end case 'J'
                    case 'R': {
                        string inputname;
                        cin >> inputname;
                        string command = "REMOVE";
                        bool tableExist = table_exist_check(command, inputname);
                        if(!tableExist) {
                            break;
                        }
                        remove_input_management(inputname);
                        break;
                    } // end case 'R'
                    default: {
                        if(first == 'Q') {
                            break;
                        } else {
                        cout << "Error: unrecognized command" << endl;
                        break;
                        }
                    } // end default case
                }

            } while (cmd != "QUIT");
        }

        void create_input_management(string &name, int &number) 
        {
            string coltype;
            string colname;
            string temp = "";
            
            // Checking to make sure a table with the same name doesn't already exist
            auto iter = tables.find(name);
            string junk2;
            if(iter != tables.end()) {
                getline(cin, junk2);
                cout << "Error during CREATE: ";
                cout << "Cannot create already existing table " << name << endl;
                return;
            }
            // If not, create the table

            tables[name];
            for(size_t i = 0; i < static_cast <size_t> (number); i++) {
                cin >> coltype;
                if(coltype == "int") {
                    pair<string, Metadata> insertionPair;
                    insertionPair.second.datatype = Metadata::Datatype::Int;
                    tables[name].columns.push_back(insertionPair);
                } // end case "int"
                else if(coltype == "string") {
                    pair<string, Metadata> insertionPair;
                    insertionPair.second.datatype = Metadata::Datatype::String;
                    tables[name].columns.push_back(insertionPair);
                } // end case "string"
                else if(coltype == "bool") {
                    pair<string, Metadata> insertionPair;
                    insertionPair.second.datatype = Metadata::Datatype::Bool;
                    tables[name].columns.push_back(insertionPair);
                } // end case "bool"
                else if(coltype == "double") {
                    pair<string, Metadata> insertionPair;
                    insertionPair.second.datatype = Metadata::Datatype::Double;
                    tables[name].columns.push_back(insertionPair);
                } // end case "double"
            }
            tables[name].bstIndex = false;
            tables[name].hashIndex = false;
            tables[name].orderedMapIndexColNum = -1;
            tables[name].unorderedMapIndexColNum = -1;

            cout << "New table " << name << " with column(s) ";
           
            for(size_t i = 0; i < static_cast <size_t> (number); i++) {
                cin >> colname;
                tables[name].columns[i].first = colname;
                cout << tables[name].columns[i].first << " ";
            }
            cout << "created" << endl;
        }

        void input_input_management(string &inputname, int &inputnumber) {
            // Checking to make sure a table with the name provided exists
            auto iter = tables.find(inputname);
            string junk2;
            if(iter == tables.end()) {
                cout << "Error during INSERT: ";
                cout << inputname << " does not name a table in the database" << endl;
                return;
            }
            // If it does, insert the requested data

            auto it = tables.find(inputname);

            // Preventing excess memory using reserve 
            size_t currentSize = it->second.data.size();
            it->second.data.reserve(currentSize + static_cast<size_t>(inputnumber));

            // Reading in proper datatype
            size_t horizontalSize = it->second.columns.size();
            
            for(size_t i = currentSize; i < currentSize + static_cast<size_t>(inputnumber); i++) {
                vector<TableEntry> tempVec;
                tempVec.clear();
                for(size_t j = 0; j < horizontalSize; j++) {
                    if(it->second.columns[j].second.datatype == Metadata::Datatype::Int) {
                        int num;
                        cin >> num;
                        tempVec.emplace_back(num);
                    } else if(it->second.columns[j].second.datatype == Metadata::Datatype::String) {
                        string sentence;
                        cin >> sentence;
                        tempVec.emplace_back(sentence);
                    } else if(it->second.columns[j].second.datatype == Metadata::Datatype::Bool) {
                        string tf;
                        bool tf2;
                        cin >> tf;
                        if(tf == "true") {
                            tf2 = true;
                        } else if(tf == "false") {
                            tf2 = false;
                        } else {
                            return;
                        }
                        tempVec.emplace_back(tf2);
                    } else if(it->second.columns[j].second.datatype == Metadata::Datatype::Double) {
                        double dec;
                        cin >> dec;
                        tempVec.emplace_back(dec);
                    }
                }
                it->second.data.emplace_back(tempVec);
            }

            size_t col = 0;

            if(it->second.bstIndex == true) {
                col = static_cast<size_t>(it->second.orderedMapIndexColNum);
                for(size_t i = currentSize; i < it->second.data.size(); i++) {
                    TableEntry te = it->second.data[i][col];
                    it->second.orderedMapIndex[te].push_back(static_cast<int>(i));
                }
            } else if(it->second.hashIndex == true) {
                col = static_cast<size_t>(it->second.unorderedMapIndexColNum);
                for(size_t i = currentSize; i < it->second.data.size(); i++) {
                    TableEntry te = it->second.data[i][col];
                    it->second.unorderedMapIndex[te].push_back(static_cast<int>(i));
                }
            }


            cout << "Added " << inputnumber << " rows to " << inputname;
            cout << " from position " << currentSize << " to ";
            cout << it->second.data.size() - 1 << endl;

        }

        void print_input_management(string &inputname, int &colNums) {
            // Make space in vector for all column names requested to be printed,
            vector<string> colNames;
            colNames.reserve(static_cast<size_t>(colNums));
            vector<int> indicies;
            indicies.reserve(static_cast<size_t>(colNums));

            auto it = tables.find(inputname);
            
            // Take in those names and store them into the vector + checking if they exist 
            bool colLegit;
            for(size_t i = 0; i < static_cast<size_t>(colNums); i++) {
                string colName;
                cin >> colName;
                string newC = "PRINT";
                colLegit = column_exist_check(newC, colName, inputname);
                if(!colLegit) {
                    return;
                }
                colNames.push_back(colName);
            }
            
            // Get rid of the 'where', unless its 'all', then do all:
            string whereOrAll;
            cin >> whereOrAll;
            if(whereOrAll == "ALL") {
                print_all(colNames, indicies, inputname);
                return;
            }


            // If it's 'where', then find all the indicies of the columns that were requested
            // But first, deal with the cin of column name, operation, and value
            string primaryName;
            char operation;
            cin >> primaryName >> operation;

            // Making sure primaryName is a real column in the table 
            bool primaryLegit;
            string print = "PRINT";
            primaryLegit = column_exist_check(print, primaryName, inputname);
            if(!primaryLegit) {
                return;
            }

            // Get indicies of columns in the colNames vector
            for(size_t i = 0; i < colNames.size(); i++) {
                for(size_t j = 0; j < it->second.columns.size(); j++) {
                    if(colNames[i] == it->second.columns[j].first) {
                        indicies.push_back(static_cast<int>(j));
                    }
                }
            }
            // I don't know what datatype to take in
            // We have helpers for that
            // First I have to find the index of that column

            // Finding index of the primary column (one with the operation on it)
            int temp = 0;
            for(size_t i = 0; i < it->second.columns.size(); i++) {
                if(it->second.columns[i].first == primaryName) {
                    temp = static_cast<int>(i);
                }
            }

            // Now to do the 4-way split for datatypes
            if(it->second.columns[static_cast<size_t>(temp)].second.datatype == Metadata::Datatype::Int) {
                TableEntry intTE = int_helper();
                print_comparisons(indicies, inputname, temp, intTE, operation);
                return;
            } else if(it->second.columns[static_cast<size_t>(temp)].second.datatype == Metadata::Datatype::Double) {
                TableEntry doubles = double_helper();
                print_comparisons(indicies, inputname, temp, doubles, operation);
                return;
            } else if(it->second.columns[static_cast<size_t>(temp)].second.datatype == Metadata::Datatype::Bool) {
                TableEntry tOrF = bool_helper();
                print_comparisons(indicies, inputname, temp, tOrF, operation);
                return;
            } else {
                TableEntry strings = string_helper();
                print_comparisons(indicies, inputname, temp, strings, operation);
                return;
            }
        }

        void print_all(vector<string>& colNames, vector<int> indicies, string &inputname) {
            auto it = tables.find(inputname);

                for(size_t i = 0; i < colNames.size(); i++) {
                    if(!quiet) {
                        cout << colNames[i] << " ";
                    }
                }
                if(!quiet) {
                    cout << endl;
                }

                for(size_t i = 0; i < colNames.size(); i++) {
                    for(size_t j = 0; j < it->second.columns.size(); j++) {
                        if(colNames[i] == it->second.columns[j].first) {
                            indicies.push_back(static_cast<int>(j));
                        }
                    }
                }
                int numRows = 0;
                bool flag = false;
                for(size_t i = 0; i < it->second.data.size(); i++) {
                    flag = false;
                    for(size_t j = 0; j < indicies.size(); j++) {
                        if(it->second.columns[static_cast<size_t>(indicies[j])].second.datatype == Metadata::Datatype::Bool) {
                            if(it->second.data[i][static_cast<size_t>(indicies[j])] == false) {
                                if(!quiet) {
                                    cout << "false "; 
                                }
                                flag = true;
                            } else {
                                if(!quiet) {
                                    cout << "true ";
                                }
                                flag = true;
                            }
                        } else {
                            if(!quiet) {
                                cout << it->second.data[i][static_cast<size_t>(indicies[j])] << " ";
                            }
                            flag = true;
                        }

                    }
                    if(flag) {
                        if(!quiet) {
                            cout << endl;
                        }
                        numRows++;
                    }

                }
                cout << "Printed " << numRows << " matching rows from " << inputname << endl;
                return;
        }
        
        void print_comparisons(vector<int>& indicies, string &inputname, int &temp, TableEntry te, char &op) {
            // Okay, so now we have a TableEntry object to compare with...
            // Now, utilize the functors
            // Making comparisons between TableEntry object and every value of 
            // the specified column and printing from our vector of indicies

            auto it = tables.find(inputname);

            // Printing column names
            bool temp2;
            for(size_t i = 0; i < indicies.size(); i++) {
                if(!quiet) {
                    cout << it->second.columns[static_cast<size_t>(indicies[i])].first << " ";
                }
            }
            if(!quiet) {
                cout << endl;
            }

            int numRows = 0;

            // If generate index was done with type hash (only needs to be done with equals)
            if(it->second.unorderedMapIndexColNum == temp) {

                bool flag = false;
                if(op == '=') {;
                    for(size_t i = 0; i < it->second.unorderedMapIndex[te].size(); i++) {
                        flag = false;
                        for(size_t j = 0; j < indicies.size(); j++) {
                            if(it->second.columns[static_cast<size_t>(indicies[j])].second.datatype == Metadata::Datatype::Bool) {
                                if(it->second.data[static_cast<size_t>(it->second.unorderedMapIndex[te][i])][static_cast<size_t>(indicies[j])] == false) {
                                    if(!quiet) {
                                        cout << "false ";
                                    }
                                        flag = true;
                                    } else {
                                        if(!quiet) {
                                            cout << "true ";
                                        }
                                        flag = true;
                                    }
                            } else {
                           
                            if(!quiet) {
                                cout << it->second.data[static_cast<size_t>(it->second.unorderedMapIndex[te][i])][static_cast<size_t>(indicies[j])] << " ";
                            }
                             flag = true;
                            }
                        }
                        if(flag) {
                            numRows++;
                            if(!quiet) {
                                cout << endl;
                            }
                        }
                    }
                    cout << "Printed " << numRows << " matching rows from " << inputname << endl;
                    return;
                }
            }
            // If generate index was done with type bst (all operations)
            if(it->second.orderedMapIndexColNum == temp) {
                if(op == '=') {
                    bool flag = false;
                    for(size_t i = 0; i < it->second.orderedMapIndex[te].size(); i++) {
                        flag = false;
                        for(size_t j = 0; j < indicies.size(); j++) {
                            if(it->second.columns[static_cast<size_t>(indicies[j])].second.datatype == Metadata::Datatype::Bool) {
                                if(it->second.data[static_cast<size_t>(it->second.orderedMapIndex[te][i])][static_cast<size_t>(indicies[j])] == false) {
                                    if(!quiet) {
                                        cout << "false "; 
                                    }
                                    flag = true;
                                } else {
                                    if(!quiet) {
                                        cout << "true ";
                                    }
                                    flag = true;
                                }
                            } else {
                                if(!quiet) {
                                    cout << it->second.data[static_cast<size_t>(it->second.orderedMapIndex[te][i])][static_cast<size_t>(indicies[j])] << " ";
                                }
                                flag = true;
                            }
                        }
                        if(flag) {
                            numRows++;
                            if(!quiet) {
                                cout << endl;
                            }
                        }
                    }
                    cout << "Printed " << numRows << " matching rows from " << inputname << endl;
                    return;
                } else if(op == '>') {
                    bool flag = false;
                    auto lowerIt = it->second.orderedMapIndex.upper_bound(te);
                    auto maxIt = it->second.orderedMapIndex.end();
                    for(auto i = lowerIt; i != maxIt; i++) {
                         for(size_t j = 0; j < i->second.size(); j++) {
                             flag = false;
                             for(size_t c = 0; c < indicies.size(); c++) {
                                if(it->second.columns[static_cast<size_t>(indicies[c])].second.datatype == Metadata::Datatype::Bool) {
                                if(it->second.data[static_cast<size_t>(i->second[j])][static_cast<size_t>(indicies[c])] == false) {
                                    if(!quiet) {
                                        cout << "false "; 
                                    }
                                    flag = true;
                                } else {
                                    if(!quiet) {
                                        cout << "true ";
                                    }
                                    flag = true;
                                }
                            } else {
                                if(!quiet) {
                                    cout << it->second.data[static_cast<size_t>(i->second[j])][static_cast<size_t>(indicies[c])] << " ";
                                }
                                flag = true;
                            }
                             }
                         if(flag) {
                                numRows++;
                                if(!quiet) {
                                    cout << endl;
                                }
                            }
                         }

                    }
                    cout << "Printed " << numRows << " matching rows from " << inputname << endl;
                    return;
                } else if(op == '<') {
                    bool flag = false;
                    auto lowerIt = it->second.orderedMapIndex.begin();
                    auto maxIt = it->second.orderedMapIndex.lower_bound(te);
                    for(auto i = lowerIt; i != maxIt; i++) {
                         for(size_t j = 0; j < i->second.size(); j++) {
                            flag = false;
                             for(size_t c = 0; c < indicies.size(); c++) {
                                if(it->second.columns[static_cast<size_t>(indicies[c])].second.datatype == Metadata::Datatype::Bool) {
                                if(it->second.data[static_cast<size_t>(i->second[j])][static_cast<size_t>(indicies[c])] == false) {
                                    flag = true;
                                    if(!quiet) {
                                        cout << "false "; 
                                    }
                                } else {
                                    flag = true;
                                    if(!quiet) {
                                        cout << "true ";
                                    }
                                }
                            } else {
                               flag = true;
                               if(!quiet) {
                                    cout << it->second.data[static_cast<size_t>(i->second[j])][static_cast<size_t>(indicies[c])] << " ";
                               }
                            }
                             }
                        if(flag) {
                             numRows++;
                             if(!quiet) {
                                cout << endl;
                             }
                         }
                         }
                    }
                    cout << "Printed " << numRows << " matching rows from " << inputname << endl;
                    return;
                }
            }

            // Printing without a generated index 
            bool flag = false;
            for(size_t i = 0; i < it->second.data.size(); i++) {
                flag = false;
                for(size_t j = 0; j < indicies.size(); j++) {
                    if(op == '=') {
                        temp2 = equals_helper(te, it->second.data[i][static_cast<size_t>(temp)]);
                        if(temp2 == true) {
                            if(it->second.columns[static_cast<size_t>(indicies[j])].second.datatype == Metadata::Datatype::Bool) {
                                if(it->second.data[i][static_cast<size_t>(indicies[j])] == false) {
                                    flag = true;
                                    if(!quiet) {
                                        cout << "false "; 
                                    }
                                } else {
                                    flag = true;
                                    if(!quiet) {
                                        cout << "true ";
                                    }
                                }
                            } else {
                                flag = true;
                                if(!quiet) {
                                    cout << it->second.data[i][static_cast<size_t>(indicies[j])] << " ";
                                }
                            }
                            
                        }
                    }
                    if(op == '>') {
                        temp2 = less_helper(te, it->second.data[i][static_cast<size_t>(temp)]);
                        if(temp2 == true) {
                            if(it->second.columns[static_cast<size_t>(indicies[j])].second.datatype == Metadata::Datatype::Bool) {
                                if(it->second.data[i][static_cast<size_t>(indicies[j])] == false) {
                                    if(!quiet) {
                                        cout << "false "; 
                                    }
                                    flag = true;
                                } else {
                                    if(!quiet) {
                                        cout << "true ";
                                    }
                                    flag = true;
                                }
                            } else {
                                flag = true;
                                if(!quiet) {
                                    cout << it->second.data[i][static_cast<size_t>(indicies[j])] << " ";
                                }
                            }
                        }
                    }
                    if(op == '<') {
                        temp2 = greater_helper(te, it->second.data[i][static_cast<size_t>(temp)]);
                        if(temp2 == true) {
                            if(it->second.columns[static_cast<size_t>(indicies[j])].second.datatype == Metadata::Datatype::Bool) {
                                if(it->second.data[i][static_cast<size_t>(indicies[j])] == false) {
                                    if(!quiet) {
                                        cout << "false "; 
                                    }
                                    flag = true;
                                } else {
                                    if(!quiet) {
                                        cout << "true ";
                                    }
                                    flag = true;
                                }
                            } else {
                                if(!quiet) {
                                    cout << it->second.data[i][static_cast<size_t>(indicies[j])] << " ";
                                }
                            flag = true;
                            }
                        }
                    }
                }
                if(flag) {
                    if(!quiet) {
                        cout << endl;
                    }
                    numRows++;
                }
            }
            cout << "Printed " << numRows << " matching rows from " << inputname << endl;
            return;
        }
       
        void delete_input_management(string &inputname) {
            // Take in the column name and operation
            string primaryName;
            char operation;
            cin >> primaryName >> operation;

            // Checking if primaryName is in the table
            string strDel = "DELETE";
            bool colExist = column_exist_check(strDel, primaryName, inputname);
            if(!colExist) {
                return;
            }
            
            auto it = tables.find(inputname);

            // Get the index of the column, could this be avoided by an index member variable?
            int temp = 0;
            for(size_t i = 0; i < it->second.columns.size(); i++) {
                if(it->second.columns[i].first == primaryName) {
                    temp = static_cast<int>(i);
                }
            }

            // Okay, now to do the 4-way split for datatypes
            if(it->second.columns[static_cast<size_t>(temp)].second.datatype == Metadata::Datatype::Int) {
                TableEntry intTE = int_helper();
                delete_if(inputname, temp, intTE, operation);
                return;
            } else if(it->second.columns[static_cast<size_t>(temp)].second.datatype == Metadata::Datatype::Double) {
                TableEntry doubles = double_helper();
                delete_if(inputname, temp, doubles, operation);
                return;
            } else if(it->second.columns[static_cast<size_t>(temp)].second.datatype == Metadata::Datatype::Bool) {
                TableEntry tOrF = bool_helper();
                delete_if(inputname, temp, tOrF, operation);
                return;
            } else {
                TableEntry strings = string_helper();
                delete_if(inputname, temp, strings, operation);
                return;
            }
        }

        void delete_if(string &inputname, int &temp, TableEntry &te, char &operation) {

            auto iter = tables.find(inputname);

            // For purpose of the final print
            size_t dataSize = iter->second.data.size();
                if(operation == '=') {
                    auto it = iter->second.data.begin();
                    auto it2 = iter->second.data.end();
                    equalFunctor equal(temp, te);
                    iter->second.data.erase(remove_if(it, it2, equal), it2);
                }
                if(operation == '>') {
                    auto it = iter->second.data.begin();
                    auto it2 = iter->second.data.end();
                    lessFunctor le(temp, te);
                    iter->second.data.erase(remove_if(it, it2, le), it2);
                }
                if(operation == '<') {
                    auto it = iter->second.data.begin();
                    auto it2 = iter->second.data.end();
                    greaterFunctor gr(temp, te);
                    iter->second.data.erase(remove_if(it, it2, gr), it2);
                }
            size_t newDataSize = iter->second.data.size();

                cout << "Deleted " << dataSize - newDataSize << " rows from " << inputname << endl;

                if(iter->second.hashIndex || iter->second.bstIndex) {
                    generate_index_post_delete(inputname);
                }

            }
        
        void remove_input_management(string &inputname) {
            tables.erase(inputname);
            cout << "Table " << inputname << " deleted" << endl;
            return; 
        }
        
        void generate_index_management(string &inputname, string &type, string &colName) {

            auto it = tables.find(inputname);

            it->second.orderedMapIndex.clear();
            it->second.unorderedMapIndex.clear();
            it->second.orderedMapIndexColNum = -1;
            it->second.unorderedMapIndexColNum = -1;
            it->second.hashIndex = false;
            it->second.bstIndex = false;


            if(type == "hash") {
                it->second.hashIndex = true;

                size_t temp = 0;
                for(size_t i = 0; i < it->second.columns.size(); i++) {
                    if(it->second.columns[i].first == colName) {
                        temp = i;
                    }
                }
                it->second.unorderedMapIndexColNum = static_cast<int>(temp);
                for(size_t i = 0; i < it->second.data.size(); i++) {
                    it->second.unorderedMapIndex[it->second.data[i][temp]].push_back(static_cast<int>(i));
                }
                cout << "Created hash index for table " << inputname << " on column " << colName << endl;
                return;
                
            }
            if(type == "bst") {
                it->second.bstIndex = true;

                size_t temp2 = 0;
                for(size_t i = 0; i < it->second.columns.size(); i++) {
                    if(it->second.columns[i].first == colName) {
                        temp2 = i;
                    }
                }
                for(size_t i = 0; i < it->second.data.size(); i++) {

                    it->second.orderedMapIndex[it->second.data[i][temp2]].push_back(static_cast<int>(i));
                }
                it->second.orderedMapIndexColNum = static_cast<int>(temp2);
                cout << "Created bst index for table " << inputname << " on column " << colName << endl;
                return;
            }
        }
        
        void generate_index_post_delete(string &inputname) {
            auto it = tables.find(inputname);
            if(it->second.hashIndex) {
                it->second.unorderedMapIndex.clear();
                size_t temp = static_cast<size_t>(it->second.unorderedMapIndexColNum);
                for(size_t i = 0; i < it->second.data.size(); i++) {
                    it->second.unorderedMapIndex[it->second.data[i][temp]].push_back(static_cast<int>(i));
                }
                return;
                
            }
            else if(it->second.bstIndex) {
                it->second.orderedMapIndex.clear();
                size_t temp2 = static_cast<size_t>(it->second.orderedMapIndexColNum);
                for(size_t i = 0; i < it->second.data.size(); i++) {
                    it->second.orderedMapIndex[it->second.data[i][temp2]].push_back(static_cast<int>(i));
                }
                return;
            }
        }
        
        void join_input_management() {

            // All input management and error checking
            string junk, tablename1, tablename2, colname1, colname2;
            int n;
            char otherjunk;
            string command = "JOIN";

            cin >> tablename1;
            if(!table_exist_check(command, tablename1)) {
                return;
            }
            cin >> junk >> tablename2;
            if(!table_exist_check(command, tablename2)) {
                return;
            }
            cin >> junk >> colname1;
            if(!column_exist_check(command, colname1, tablename1)) {
                return;
            }
            cin >> otherjunk >> colname2;
            if(!column_exist_check(command, colname2, tablename2)) {
                return;
            }
            cin >> junk >> junk >> n;

            // Storing print_colnameN and their respective '1's or '2's into pairs
            vector<pair<int, int>> printColNamePairs;
            string printcolname;
            int whichTable;
            printColNamePairs.reserve(static_cast<size_t>(n));
            for(size_t i = 0; i < static_cast<size_t>(n); i++) {
                cin >> printcolname >> whichTable;
                if(whichTable == 1) {
                    if(!column_exist_check(command, printcolname, tablename1)) {
                        return;
                    }
                    for(size_t j = 0; j < tables[tablename1].columns.size(); j++) {
                        if(tables[tablename1].columns[j].first == printcolname) {
                            pair<int, int> tempPair;
                            tempPair.first = static_cast<int>(j);
                            tempPair.second = whichTable;
                            printColNamePairs.push_back(tempPair);
                            break;
                        }
                    }
                } else if(whichTable == 2) {
                    if(!column_exist_check(command, printcolname, tablename2)) {
                        return;
                    }
                    for(size_t k = 0; k < tables[tablename2].columns.size(); k++) {
                        if(tables[tablename2].columns[k].first == printcolname) {
                            pair<int, int> tempPair;
                            tempPair.first = static_cast<int>(k);
                            tempPair.second = whichTable;
                            printColNamePairs.push_back(tempPair);
                            break;
                        }
                    }
                }  
            }

            // Find index of column
            int colTab1 = 0;
            int colTab2 = 0;
            for(size_t i = 0; i < tables[tablename1].columns.size(); i++) {
                if(tables[tablename1].columns[i].first == colname1) {
                    colTab1 = static_cast<int>(i);
                }
            }

            for(size_t i = 0; i < tables[tablename2].columns.size(); i++) {
                if(tables[tablename2].columns[i].first == colname2) {
                    colTab2 = static_cast<int>(i);
                }
            }


            // Doing generate index on the new column 
            unordered_map<TableEntry, vector<int>> h;
            for(size_t i = 0; i < tables[tablename2].data.size(); i++) {
                h[tables[tablename2].data[i][static_cast<size_t>(colTab2)]].push_back(static_cast<int>(i));
            }
            for(size_t i = 0; i < printColNamePairs.size(); i++) {
                if(printColNamePairs[i].second == 1) {
                    if(!quiet) {
                        cout << tables[tablename1].columns[static_cast<size_t>(printColNamePairs[i].first)].first << " ";
                    }
                } else if(printColNamePairs[i].second == 2) {
                    if(!quiet) {
                        cout << tables[tablename2].columns[static_cast<size_t>(printColNamePairs[i].first)].first << " ";
                    }
                }
            }
            if(!quiet) {
                cout << endl;
            }

            int numRowsPrint = 0;

            for(size_t i = 0; i < tables[tablename1].data.size(); i++) {
                if(h.find(tables[tablename1].data[i][static_cast<size_t>(colTab1)]) != h.end()) {
                    for(size_t j = 0; j < h[tables[tablename1].data[i][static_cast<size_t>(colTab1)]].size(); j++) {
                        for(size_t k = 0; k < printColNamePairs.size(); k++) {
                            if(printColNamePairs[k].second == 1) {
                                if(tables[tablename1].columns[static_cast<size_t>(printColNamePairs[k].first)].second.datatype == Metadata::Datatype::Bool) {
                                    if(tables[tablename1].data[i][static_cast<size_t>(printColNamePairs[k].first)] == false) {
                                        if(!quiet) {
                                            cout << "false ";
                                        }
                                    } else {
                                        if(!quiet) {
                                            cout << "true ";
                                        }
                                    }
                                } else {
                                    if(!quiet) {
                                        cout << tables[tablename1].data[i][static_cast<size_t>(printColNamePairs[k].first)]<< " ";
                                    }

                                }
                            } else if(printColNamePairs[k].second == 2) {
                                int row = h[tables[tablename1].data[i][static_cast<size_t>(colTab1)]][j];
                                if(tables[tablename2].columns[static_cast<size_t>(printColNamePairs[k].first)].second.datatype == Metadata::Datatype::Bool) {
                                    if(tables[tablename2].data[static_cast<size_t>(row)][static_cast<size_t>(printColNamePairs[k].first)] == false) {
                                        if(!quiet) {
                                            cout << "false ";
                                        }
                                    } else {
                                        if(!quiet) {
                                            cout << "true ";
                                        }
                                    }
                                } else {
                                    if(!quiet) {
                                        cout << tables[tablename2].data[static_cast<size_t>(row)][static_cast<size_t>(printColNamePairs[k].first)] << " ";
                                    }
                                }
                            }
                        }
                        if(!quiet) {
                            cout << endl;
                        }
                        numRowsPrint++;
                    }
                } else {
                    continue;
                }
            }
            cout << "Printed " << numRowsPrint << " rows from joining " << tablename1 << " to " << tablename2 << endl;
            return;
        }
       
        TableEntry string_helper() {
            string a;
            cin >> a;
            TableEntry c = TableEntry(a);
            return c;
        }

        TableEntry bool_helper() {
            string a;
            bool b;
            cin >> a;
            if(a == "true") {
                b = true;
            } else {
                b = false;
            }
            TableEntry c = TableEntry(b);
            return c;
        }

        TableEntry int_helper() {
            int a;
            cin >> a;
            TableEntry c = TableEntry(a);
            return c;
        }

        TableEntry double_helper() {
            double a;
            cin >> a;
            TableEntry c = TableEntry(a);
            return c;
        }

        bool equals_helper (const TableEntry &te1, const TableEntry &te2) const {
            return te1 == te2;
        }

        bool greater_helper (const TableEntry &te1, const TableEntry &te2) const {
            return te1 > te2;
        }

        bool less_helper (const TableEntry &te1, const TableEntry &te2) const {
            return te1 < te2;
        }

        bool table_exist_check(string &command, string &tablename) {
            auto iter = tables.find(tablename);
            string junk2;
            if(iter == tables.end()) {
                getline(cin, junk2);
                cout << "Error during " << command << ": " << tablename;
                cout << " does not name a table in the database" << endl;
                return false;
            }
            return true;
        }

        bool column_exist_check(string &command, string &colName, string &tablename) {
            for(size_t i = 0; i < tables[tablename].columns.size(); i++) {
                if(tables[tablename].columns[i].first == colName) {
                    return true;
                }
            }
            string junk;
            getline(cin, junk);
            cout << "Error during " << command << ": " << colName;
            cout << " does not name a column in " << tablename << endl; 
            return false;
        }

};

int main(int argc, char *argv[]) {
    // I/O Speedup
    ios_base::sync_with_stdio(false);

    // Declaring and initializing an instance of the primary class
    Primary p1;
    p1.set_defaults();
    p1.option_management(argc, argv);
    p1.SQL();
    cout << "Thanks for using the relational database!" << endl;

    return 0;
}