diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..761cbd4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.out +*.o +*.db +SemiSQL diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e8beb6b --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +CXXFLAGS=-Wall -Wextra -pedantic -std=c++11 + +all: SemiSQL.o + $(CXX) SemiSQL.o -o SemiSQL + +SemiSQL.o: SemiSQL.cpp + $(CXX) $(CXXFLAGS) -c SemiSQL.cpp + +clean: + $(RM) SemiSQL *.o diff --git a/README.md b/README.md index 5fecb2e..915122e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,32 @@ -# Semi_SQL -This code mimics SQL in a very basic way using c++ +# SemiSQL +This code basically mimics a SQL DBMS using C++ in a CLI approach. + As of now understand the working of the code by yourself detailed working will be published later with better updates. + +This project is in active development and many of the features aren't implemented. Use at your own risk. + +## Plan +### Operations +- [x] Create Database +- [x] Open Database +- [x] Create Table +- [x] Show Tables +- [x] Select Table +- [x] Display Table +- [x] Select specific Field of Table +- [ ] Drop Table +- [ ] Drop Column +### Functions +- [ ] ADD() +- [ ] AVERAGE() + +## Dependencies +None, use any C++ 11 compatible compiler. + +## Building +```sh +$ make +``` + +Developed and tested under Linux environment. diff --git a/SemiSQL.cpp b/SemiSQL.cpp index e8d092b..fcd4de6 100644 --- a/SemiSQL.cpp +++ b/SemiSQL.cpp @@ -1,329 +1,349 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -/* -create database done -create table done -show tables done -select *table done -select table done -delete table -delete column -add,average,percentage -*/ -void syntax(char a[]); -void database(char cj[]); -void createtable(char i[]); -void shreadtable(char gh[], char tt[]); -int linecr(char x[],char j[]); -void main() -{ - clrscr(); - _setcursortype(_NOCURSOR); - int i=0; - while(1) - { - gotoxy(15,6); - cout << "\t! READ THIS !"; - gotoxy(15,7); - if(i==0) - i++; - cprintf("********************************************************"); - textcolor(i); - gotoxy(15,8); - cprintf("Type CREATEDB to create a database."); - textcolor(i+1); - gotoxy(15,9); - cprintf("Type OPENDB to open the database."); - textcolor(i+2); - gotoxy(15,10); - cprintf("Type CREATETABLE to create the table."); - textcolor(i+3); - gotoxy(15,11); - cprintf("Type OPENTABLE to open the table."); - textcolor(i+4); - gotoxy(15,12); - cprintf("Type DPTABLE to display the table."); - textcolor(i+5); - gotoxy(15,13); - cprintf("Type SHOWTABLES to display the names of all the tables"); - textcolor(i+6); - gotoxy(15,14); - cprintf("Type SELECTTABLE to select a specific table."); - textcolor(i+7); - gotoxy(15,15); - cprintf("Type SELECTCOLUMN to select a column name."); - textcolor(i+8); - gotoxy(15,16); - cprintf("Type EXIT to exit."); - textcolor(i); - gotoxy(15,17); - i++; - cprintf("********************************************************"); - delay(2000); - if(i > 5 ) break; - } - cout << "\nNow start entering data\n"; - char a[10]; - gets(a); - syntax(a); - getch(); +#include +#include +#include +#include +#include +const std::string DEFAULT_PROMPT_TEXT = "semi_sql"; +const std::string DB_TABLE_SEP = "::"; +const std::string PROMPT_SEP = "> "; +const std::string DB_EXT = ".db"; +const std::string DELIMS = ",;}"; + +int parse(std::string const&); +void createdb(std::string const&); +void opendb(std::string const&); +void create_table(void); +void show_tables(void); +void select_table(void); +void display_table(void); +void select_field(void); +void navigate_to_table(std::ifstream&, std::string const&); +void print_row(std::vector const&); +void get_tables(std::vector&); +bool db_not_opened(void); +bool table_not_selected(void); +void print_prompt(void); +void tokenize(std::string const&, std::string const&, std::vector&); + +struct db { + std::string name; + std::string* table; +} *currentdb; + +int main(void) { + std::string a; + + currentdb = nullptr; + + std::cout << "\t! READ THIS !" << std::endl + << "********************************************************" << std::endl + << "* Type CREATEDB to create a database." << std::endl + << "* Type OPENDB to open the database." << std::endl + << "* Type CREATETABLE to create the table." << std::endl + << "* Type OPENTABLE to open the table." << std::endl + << "* Type DPTABLE to display the table." << std::endl + << "* Type SHOWTABLES to display the names of all the tables" << std::endl + << "* Type SELECTTABLE to select a specific table." << std::endl + << "* Type SELECTFIELD to select a field name." << std::endl + << "* Type EXIT to exit." << std::endl + << "********************************************************" << std::endl; + + do { + std::cout << std::endl; + print_prompt(); + if (!getline(std::cin, a)) { + std::cout << "EXIT" << std::endl; + break; + } + } while (parse(a)); + + if (currentdb) { + if (currentdb->table) + delete currentdb->table; + delete currentdb; + } + + std::cout << "OK, Bye!" << std::endl; + return 0; } -void database(char db[]) -{ - char z[20]; - int j=strlen(db); - for (int k=0,a=k+1;kj || line[i] == ';') break; - if(k == j)cout << line[i]; - i++; - }i=0; - cout << "\n" ; - delay(2000); - } - file.close(); - gets(line); - syntax(line); + +void createdb(std::string const &dbname) { + std::fstream dbfile(dbname + DB_EXT, std::ios::in); + + if (dbfile) { + std::cout << "Database exists!" << std::endl; + dbfile.close(); + return; + } + + dbfile.open(dbname + DB_EXT, std::ios::out); + dbfile.close(); + + std::cout << "Database created!" << std::endl; } -void createtable(char db[]) -{ - char a[10]; - int i=0,n=0,k=0; - cout << "\nEnter table name\n"; - gets(a); - ofstream fout; - fout.open(db,ios::app|ios::nocreate); - if(!fout) - {cout<<"\nNO dB OPENED!!\n";}; //syntax - fout << "@"<< a << "@\n"; - cout<<"\nEnter column names\n"; //excess data warning; - gets(a); - while(a[i] != '\0') - { - if(a[i] == ',') n++; - i++; - } - cout << endl; - fout << a<< "\n"; - cout <<"\nEnter column data\n"; - i=0; - do - { - maaza: - k= 0; - gets(a); - while(a[i] != '\0') - { - if(a[i] == ',') k++; - if(k > n){ cout <<"\nYou're entering excess data!" - << "\n Recheck your data\n"; goto maaza;} - i++; - }i=0; - fout << a; - while(a[i] != '\0') - { - if(a[i] == '}'){fout << '\n' ;goto cas;} - i++; - } - if(a[i] == '\0') fout << '\n'; - i=0; - }while(1); - cas: - fout.close(); - gets(a); - syntax(a); + +void opendb(std::string const &dbname) { + std::fstream dbfile(dbname + DB_EXT, std::ios::in); + + if (!dbfile) { + std::cout << "DB not created!" << std::endl; + return; + } + + if (currentdb) delete currentdb; + currentdb = new db; + currentdb->name = dbname; + currentdb->table = nullptr; } -void linecr(char line[]) -{ - char trans[10]; - int n,i=0; - strcpy(trans,line); - n = strlen(trans); - trans[n-1] = '\0'; - while(trans[i] != '\0') - { - trans[i] = trans[i+1]; - i++; - } - strcpy(line,trans); + +void create_table(void) { + if (db_not_opened()) return; + + std::string table_name, cscols, csdata; + int field_count; + + std::cout << "Enter table name: "; + getline(std::cin, table_name); + + std::ofstream fout(currentdb->name + DB_EXT, std::ios::app); + + fout << "@" << table_name << "@" << std::endl; + + std::cout << "Enter column names:" << std::endl; + getline(std::cin, cscols); + field_count = std::count(cscols.begin(), cscols.end(), ',') + 1; + + fout << cscols << std::endl; + + std::cout << std::endl << "Enter column data:" << std::endl; + do { + bool excess_data; + + do { + getline(std::cin, csdata); + + excess_data = (std::count(csdata.begin(), csdata.end(), ',') >= field_count); + if (excess_data) + std::cout << "You're entering excess data!" << std::endl + << "Recheck your data" << std::endl; + } while (excess_data); + + fout << csdata << std::endl; + + } while (csdata.find('}') == std::string::npos); + + fout.close(); } -int linecr(char line[],char line2[]) -{ - - int j=0,i=0; - while(line2[i] != '\0') - { j++; - i++; - if(line2[i] != line[i+1])goto fun; - } - fun: - if(j == strlen(line2))return 1; - else return 0; + +void show_tables(void) { + if (db_not_opened()) return; + + std::vector tables; + get_tables(tables); + + for (size_t i = 0; i < tables.size(); i++) { + std::cout << tables[i]; + if (i == tables.size() - 1) continue; // don't print the last comma + std::cout << ","; + } + std::cout << std::endl; } -void syntax(char a[]) -{ - char syn[10],var[10],ch,line[50], line2[50]; - static char db[20],table[30]; - int i=0,k=0; - fstream infile,tout; - if(strcmp("CREATEDB",a) == 0) // done - { - cout << "\nCREATING" ; - cout << "\nEnter database name-\n"; - gets(db); - database(db); - puts(db); - infile.open(db,ios::out|ios::noreplace); - cout << "\nDatabase created!\n"; - gets(syn); - syntax(syn); - } - else if(strcmp("OPENDB",a) ==0) //done - { - cout << "\nEnter database name-\n "; - gets(db); - database(db); - tout.open(db,ios::in); - if(!tout) - { - cout << "\nDB not created!\n "; - } - else cout << "\nDatabase opened!\n"; - tout.close(); - gets(syn); - syntax(syn); - } - if(strcmp("CREATETABLE",a) ==0) - { - createtable(db); - } - if(strcmp("DPTABLE",a) == 0) - { - ifstream file(db); - if(!file) cout << "\nDB not entered!\n"; - while(1) - { - while(k != 1) - { - file.get(line,100); - file.get(ch); - if(line[0] == '@'){k=linecr(line,table);} - } - if(k==1 ) - { - file.get(line,100); - file.get(ch); - i=0; - while(line[i] != ';' ) - { - if(line[i] == ',') {cout << "\t\t"; i++;} - if(line[i] == '}') goto jojol; - cout << line[i]; - i++; - delay(100); - } - if(line[i] == ';')cout << ch; - delay(1000); - } - if(file.eof() ==1) break; - } - jojol: - gets(syn); - syntax(syn); - } - if(strcmp("SHOWTABLES",a) == 0) - { - ifstream tout(db,ios::nocreate); - tout.seekg(0); - while(1) - { - tout.get(var,100); // add variable - tout.get(ch); - if(var[0] == '@') - { linecr(var); - cout << var << ","; - } - if(tout.eof()) break; - } - tout.close(); - gets(syn); - syntax(syn); - } - if(strcmp("SELECTTABLE",a) == 0) - { - cout << "\nEnter table name-\n"; - gets(table); - gets(syn); - syntax(syn); - } - if(strcmp("SELECTCOLUMN",a) == 0) - { - shreadtable(db,table); //enter any column name and it will display the column name - } - if(strcmp("EXIT",a) == 0) exit(1); - else cout << "\t\t --WRONG SYNTAX!\n"; - gets(syn); - syntax(syn); + +void select_table(void) { + if (db_not_opened()) return; + + std::string table_name; + + std::cout << "Enter table name: "; + getline(std::cin, table_name); + + std::vector tables; + get_tables(tables); + + if (std::find(tables.begin(), tables.end(), table_name) == tables.end()) { + std::cout << "Table does not exist in database." << std::endl; + return; + } + + if (currentdb->table) delete currentdb->table; + currentdb->table = new std::string(table_name); +} + +void display_table(void) { + if (table_not_selected()) return; + + std::ifstream dbfile(currentdb->name + DB_EXT); + std::string line; + + navigate_to_table(dbfile, *(currentdb->table)); + + std::vector fields, values; + + // Get the fields and print 'em + getline(dbfile, line); + tokenize(line, DELIMS, fields); + print_row(fields); + + // Get values and print 'em + do { + getline(dbfile, line); + tokenize(line, DELIMS, values); + print_row(values); + } while (line.find('}') == std::string::npos); + + dbfile.close(); +} + +void select_field(void) { + if (table_not_selected()) return; + + std::ifstream dbfile(currentdb->name + DB_EXT); + std::string line, field_name; + + std::cout << "Enter field name: "; + getline(std::cin, field_name); + + navigate_to_table(dbfile, *(currentdb->table)); + + std::vector fields, values; + + // Get the fields + getline(dbfile, line); + tokenize(line, DELIMS, fields); + + // Get the index of `field_name` in `fields` + auto it = std::find(fields.begin(), fields.end(), field_name); + if (it == fields.end()) { + std::cout << "Field does not exist!" << std::endl; + return; + } + size_t index = it - fields.begin(); + + std::cout << fields[index] << std::endl; + + // Print values corresponding to `field_name` + do { + getline(dbfile, line); + tokenize(line, DELIMS, values); + std::cout << values[index] << std::endl; + } while (line.find('}') == std::string::npos); + + dbfile.close(); +} + +void navigate_to_table(std::ifstream &file, std::string const &table_name) { + std::string line; + + do { + getline(file, line); + + if (line[0] == '@') { + if (line.substr(1, line.size()-2) == table_name) + break; + } + } while(!file.eof()); +} + +void print_row(std::vector const &cols) { + for (size_t i = 0; i < cols.size(); i++) { + std::cout << cols[i]; + if (i != cols.size() - 1) + std::cout << "\t\t"; + } + std::cout << std::endl; +} + +void get_tables(std::vector &tables) { + std::ifstream dbfile(currentdb->name + DB_EXT); + std::string line; + + do { + getline(dbfile, line); + + if (line[0] == '@') // @'s are table name identification markers + tables.push_back(line.substr(1, line.length()-2)); + } while (!dbfile.eof()); + + dbfile.close(); +} + +bool db_not_opened(void) { + if (currentdb) return false; + std::cout << "NO dB OPENED!!" << std::endl; + return true; +} + +bool table_not_selected(void) { + if (!db_not_opened()) { + if (currentdb->table) + return false; + std::cout << "NO Table SELECTED!!" << std::endl; + } + return true; +} + +void print_prompt(void) { + if (currentdb) { + std::cout << currentdb->name; + if (currentdb->table) + std::cout << DB_TABLE_SEP << *(currentdb->table); + } else + std::cout << DEFAULT_PROMPT_TEXT; + + std::cout << PROMPT_SEP; +} + +void tokenize(std::string const &str, std::string const &delims, + std::vector &out) { + size_t start=0; + size_t end; + + out.clear(); + while ((end = str.find_first_of(delims, start)) != std::string::npos) { + out.push_back(str.substr(start, end - start)); + start = end + 1; + } + + // push_back the last element iff it's not empty + if (start != str.size()) + out.push_back(str.substr(start, str.size() - start)); }