$29
Objectives & Intended Learning Outcomes
The objective of this assignment is to familiarise you with classes and utilising their constructors & desctructors.0After this assignment you should be able to:
1. Explain the structure of a program involving classes
2. Develop multiple constructors
3. Utilise constructors and desctructors for memory management
4. Perform file I/O and linked-list operations
End of Objectives & Intended Learning Outcomes
Relational Database Management System0
Source:0https://www.stechies.com/differences-between-dbms-rdbms/
Introduction
The goal of this assignment is to implement a menu-driven Relational Database Management System (RDBMS). You can check this0Wikipedia0link0to get an understanding of what a Relational Database entails. A relational database is a0collection of0data in the form of rows and columns that make up one of more tables.0An RDBMS is an application used to interact with such relationship-driven databases.
RDBMS Concepts
It would be useful to familiarise yourselves with a few RDBMS concepts before0beginning with the assignment:
A database can be summarised as a collection of tables. These tables can subsequently be summarised as a collection of data. The columns of the tables are referred to as0attributes, fields, or simply columns. The rows, on the other hand, are called records,0entries, or simply rows. Every field has a data type associated with it and a database0admin can choose to enforce the types during entry. Every field also has a name. In our0implementation, there will be two types of data: integer and string, but note that both0types of data are stored as strings in the implementation.
Every table must also have a primary key. This is a field selected that serves as a0means of uniquely identifying each record. For example, if a table has an "ID" field0that is marked as a primary key, every record must be identifiable using their0respective entry of the "ID" field, which means every entry in the "ID" field must be 1)0unique, and 2) non-empty. In more advanced implementations, there may be0multiple fields that together form the primary key.
A database supports adding, deleting, and modifying tables. It may also support0operations like joining. In this assignment the inner join operation will be0implemented. Inner joining involves selecting two tables and using a certain field from0each table as the basis to merge the tables. For instance, if ID_1 is selected from one0table, and ID_2 is selected from another table, any records from these two tables where0ID_1 matches ID_2 will be merged and placed into a new table, essentially merging their0common data.
A table itself supports several operations like adding and deleting fields, setting0fields as the primary key, and sorting. One may also add, delete, and modify records.0These operations serve to facililate data management and organisation.
Note that a database and its tables support many more applications. The functionalities0of a database have been trimmed and selected for the purpose of this assignment.
End of Introduction
Description
Please read the FAQ and Change Log section0regularly, and do check it one day before the deadline to make sure you don't miss any0clarification, even if you have already submitted your work by then. You can also raise0questions on Piazza and remember to use the "pa6" tag.
Code structure
https://course.cse.ust.hk/comp2012h/assignments/PA6/Description/ 2/14
, 9:51 PM COMP 2012H Assignment 6: Simple Relational Database Management System
The skeleton code structure is as follows:
PA60
├── Database.cpp0
├── Database.h
├── Table.cpp0
├── Table.h0
├── String.cpp
├── String.h0
└── main.cpp0
main.cpp contains the Database interface;
Database.cpp and0Database.h0hold the Database class definition and the class function definitions respectively.
Table.cpp and0Table.h0hold the Table class definition and the class function definitions respectively.
String.cpp and0String.h0Provides the definition and functions of the String helper class.
Your job is to complete Database.cpp and Table.cpp so that the0Database supports all the intended operations.
Class structure
Please go through Database.h and Table.h for the definition of the0classes and structures. The following figure also gives a brief overview:
Coordinate system of individual Tables
Given a table object TABLE, TABLE[i][j] leads to a 2D array-like0access of its elements. In this case, i is the column index, and j0is the row index. Therefore, TABLE[i][j] returns the entry at (with indexing0starting from 0) the ith column and the jth row. And we provide0you a operator[] function to facilitate this access. TABLE[5] will0return the 6th column of the table and TABLE[5][3] will return the entry at the06th column and 4th row of the table (with indexing starting from 0).
Storing Data
A "save file" is a plaintext file that stores all the data of a database.
1. The first two lines are the name of the database and the number of tables it0has.
2. The next three lines are the name of a table, the number of columns it has,0and the number of rows it has.
3. Then comes the field names of the table, in one line, separated by a comma and a space. Every field name is followed by a single space and a number. 0 represents an
https://course.cse.ust.hk/comp2012h/assignments/PA6/Description/ 3/14
, 9:51 PM COMP 2012H Assignment 6: Simple Relational Database Management System
integer field and 1 represents a string field. The primary key field begins with an0asterisk.
The order of fields here matches the order in the table.
4. Then comes the data of the table. Each line represents a single row, and the data0for each field is separated by a comma and a space. The order of the fields will0match the order of the field names. The order of the rows here match the order in0the table.
5. The formats specified in 2, 3, and 4 are repeated for each table. The order of tables0here matches the order in the database.
6. Lastly, if a database is empty (0 table in total) it will not be followed by any0table data. On the other hand, if a table is empty (0 record in total), it will0not be followed by any record data.
7. There could be tables with some fields but no record. There won't be tables with no0field but some records.
Sample save file:
Sample_DB0
100
Table_10
50
50
*ID 0, Name 1, Year 0, Location 1, Major 10
2, Jack, 19, Switzerland, Science0
1, John, 20, UK, Engineering0
4, Alexis, 20, Israel, Fashion0
3, Jill, 22, Japan, Law0
0, Abraham, 18, USA, Politics0
Table_20
00
00
Table_30
00
00
Table_40
00
00
Table_50
40
60
Year 0, Name 1, *SID 0, Interest 10
2, John, 3, Fishing0
4, Jill, 1, Swimming0
1, Abraham, 0, Snooker0
1, Kimiko, 6, Programming0
3, Frank, 2, Basketball0
2, Alexis, 4, Football0
Table_60
00
00
Table_70
00
00
Table_80
00
00
Table_90
00
00
Table_100
60
40
*CID 0, Name 1, Location 1, Currency 1, Items_Bought 0, Cart_Size 00
0, Alexis, Israel, ILS, 10, 30
1, Kimiko, Japan, JPY, 0, 250
2, Frank, Serbia, RSD, 10, 100
3, Jack, Switzerland, CHF, 100, 2
https://course.cse.ust.hk/comp2012h/assignments/PA6/Description/ 4/14
, 9:51 PM COMP 2012H Assignment 6: Simple Relational Database Management System
String Helper Class
In order to shift the focus of this assignment to classes instead of string operations, a0custom written String class has been provided. It is functionally similar to0the std::string STL class. As STL is not covered yet at the moment, this0simpler0implementation has been given.
The String class allows you do define and manipulate char arrays in a manner0that closely resembles normal variables. It is an abstraction of the char array that handles0dynamic memory allocation, and it also provides additional functions. The class structure0and function descriptions are shown below. You don't need to implement this. But if you are0already familiar with the std::string class, please note that there might be0subtle differences and some functions are not implemented here. Therefore, please browse0through this class at least once before proceeding with the code.
class String0
{0
private:0
0 char *str;0
public:0
0 String();0
String(const char *str);0
String(const String &string);0
~String();0
int length() const;0
friend String operator+(const String &string1, const String &string2);0
friend String operator+(const String &string1, char a);0
friend std::ostream &operator<<(std::ostream &os, const String &string);0
friend std::istream &operator>>(std::istream &is, String &string);0
friend std::istream &getline(std::istream &is, String &string, char end);0
String &operator=(const String &string);0
String substr(int begin, int count) const;0
friend bool operator==(const String &string1, const String &string2);0
friend bool operator>(const String &string1, const String &string2);0
friend bool operator<(const String &string1, const String &string2);0
friend bool operator>=(const String &string1, const String &string2);0
friend bool operator<=(const String &string1, const String &string2);0
friend bool operator!=(const String &string1, const String &string2);0
• friend int strcmp(const String &string1, const String &string2); // Performs C-style comp
• char &operator[](int index); // Performs C-style accessing of characters in the String. 0
• const char &operator[](int index) const; // Performs C-style accessing of characters in t
• friend int stoi(const String &string); 0
• const char *getStr() const { return str; } // simple getter for the char array since ifst
};
String objects can be created in the following ways:
String name; // creates an empty String (same as String name = "" or String name(""))0 String name = "Some Name";0
String name("Some Name");0
String another = name;0
String another(name);0
All of the above perform deep copy and dynamic memory allocation. The destructor will deallocate0the dynamic memory used.
For the remainder of the String section, assume String name = "Some name"; and0String another
have been defined beforehand.
https://course.cse.ust.hk/comp2012h/assignments/PA6/Description/ 5/14
, 9:51 PM COMP 2012H Assignment 6: Simple Relational Database Management System
String anotherName = name + another; // stores "Some nameSome other name" in anotherName0
String anotherName = name + "Some other name"; // stores "Some nameSome other name" in anothe
String anotherName = name + 'S'; // stores "Some nameS" in anotherName
os << name << anotherName; // outputs "Some nameSome other name" to the the output stream os0 is >> name; // reads input from input stream is into name until newline character \n
getline(is, name, ','); // reads input from input stream is into name until comma ','
String anotherName = name.substr(1, 5); // stores "ome n" in anotherName0 String anotherName = another.substr(3, 7); // stores "e other" in anotherName
// Simple comparison operators0
name > another // returns false because "Some other name" comes after "Some name" alphabetica name == name // returns true because the two strings are equal0
name < another // returns true for the same reason name > another returns false
stoi(name) // returns 0 because "Some name" has no integer representaion0
String anotherName = "5000"0
stoi(anotherName) // returns 5000
Important Requirements
There are a few things you CANNOT do. Failure to observe these rules would potentially0result in ZERO mark for your assignment. Please carefully check your code before you submit0it.
You are NOT allowed to include any additional libraries.
End of Description
Part 1: Database
Implement the following functions of the Database class. Note that the0functions do not need to be implemented in this order. Nor do you need to implement part 10before part 2.
Database(const String &name, int numTables);
Create a new Database. In this function you can assume00 <= numTables && numTables < 100.
Set currentTable to nullptr, this->numTables0to be numTables, this->name to be name. Create numTables tables and name them as Table_i where0i ranges from 1 to numTables (inclusive). The first table0in the linked list (pointed by tableHead) should be0Table_1, and so on.
Database(const String &filename);
Create a new Database using file provided. Refer to the file format.
Set currentTable to nullptr.
This function may need to call the overloaded table constructor.
You can assume the file always exists and the format is correct.
https://course.cse.ust.hk/comp2012h/assignments/PA6/Description/ 6/14
, 9:51 PM COMP 2012H Assignment 6: Simple Relational Database Management System
You can make use of the >> operator or the0getline(istream&, String&, char) function to read data into a String0object from a file.
Note: you will need to make use of String::getStr() to0create the filestream object because the constructor for the filestream takes character0arrays, and not our custom String class, as a parameter.
~Database();
The destructor that deallocates all of the Table objects added to the Database.
bool addTable(Table *table);
Push the provided table to the end of the Table linked-list.
If table is a null pointer, print0Table is a null pointer.\n and return false.
If a table with the same name as table already exists in the database,0print Table with given name already exists in the database.\n and0return false.
Otherwise push the new table to the end of the linked list, increase0numTables and return true.
You may use the == operator to compare two String-type objects.
void addTable(const String &name);
Create a new table with name name.
If a table with the provided name exists in the database, print0Table with given name already exists in the database.\n and return.
Otherwise, allocate a new table object, add it to the end of the linked0list, and increase numTables.
Table *findTable(const String &name) const;
Return a pointer to the table with the name specified by name. Return0nullptr if no table with the provided name exists in the database.
void listTables() const;
Print the tables in the database in this format:
The tables currently existing in the database are:0
Table_1_Name0
Table_2_Name0
...0
Table_LastOne_Name\n
void deleteTable(const String &name);
Delete the table with the given name from the database.
If no table with the given name exists in the database, print0No such table exists in the
database.\n
Otherwise, deallocate the table, remove it from the linked-list, and decrease0numTables.
void setCurrentTable(const String &name);
https://course.cse.ust.hk/comp2012h/assignments/PA6/Description/ 7/14
, 9:51 PM COMP 2012H Assignment 6: Simple Relational Database Management System
Set currentTable to be the table specified by name.
If no table with the given name exists in the database, set0currentTable to be nullptr and print0No such table exists in the database.\n
void saveDatabase(const String &filename) const;
Save the database, its tables, and their data to a file named filename, using0the format in the Storing Data section.0You may use the << operator to output String objects and0int values to the output filestream.0Remember to prepend an asterisk to the primary key field. Remember to use commas as a0separation between fields, and \n as a separation between records.0Finally, remember that 0 represents a field of type int and 1 represents a0field of type String.
Note: you will need to make use of String::getStr() to0create the filestream object because the constructor for the filestream takes character0arrays as a parameter.
void innerJoin(Table* table1, Table* table2);
This is an optional bonus task. Perform inner join on two tables' primary keys, as0explained in the RDBMS ConceptsM section, create a new table and add0it into the0current database.
If either of the two tables is a null pointer, print0No such table exists in the database.\n and return.
If two primary keys have different types, there is an immediate mismatch. Print0Type mismatch between target fields.\n and return.
Allocate a new table with the name "[name of table1]+[name of table2]".0For instance, if table1 is named ONE and0table2 is named TWO, the new table should be named0ONE+TWO. You can assume this name will not conflict0with other tables in the database.
The first field of the new table should be keyName1+keyName2.0For instance, if the first
primary key is ID and another primary key is0Name, the new field should be named
ID+Name. You can0assume this name will not conflict with other fields in the new table. Set
it as the0primaryKey of the new table.
The subsequent fields of the new table should be all the remaining fields of0table1, followed by all the remaining fields of table2,0the relative orders of field should remain. This should hence resemble the0example below.
Note that if there is a field with the same name in table1 and0table2, there is a potential problem with duplicate field names. Hence, the corresponding field from0table1 will remain the same name, and the corresponding0field from table2 should be named [fieldName](T2). See the0examples in the figure.
The entries of the new table should be the conjunction of all pairs of records from0table1 and table20where the data in fieldName1 matches fieldName2. This can0be understood
https://course.cse.ust.hk/comp2012h/assignments/PA6/Description/ 8/14
, 9:51 PM COMP 2012H Assignment 6: Simple Relational Database Management System
using the figure above.
The order of rows in the new table should match their order in table1.
Push this new table into the database at the end of the linked0list.
Be careful about preserving typing, names, and the naming exceptions during name0matching.
You may use the + operator to concatenate two String objects. For0example, would become "deadbeef".
End of Part 1
Part 2: Table
Implement the following functions of the Table class. Note that the0functions do not need to be implemented in this order. Nor do you need to implement part 10before part 2.
Table(const String &name);
The constructor. Create a new Table.
Initilaise pointers to nullptr.
Initialise numRows and numCols to 0, and0tableSize to 100.
Set table name as provided.
Table(ifstream &ifs, const String &name);
Overloaded Table constructor that takes in an already initialised input filstream and name.0This function is related to Database::Database(const String &filename).
Set table name as provided.
Initilaise pointers to nullptr.
Read numCols, numRows, field names, and records, as described in the format0specified the in the Storing Data section.
Set tableSize to 100, in this function you can assume that number of0rows0will be less than 100.
Remember that the primary key field has been prepended with an asterisk and every field name0is followed by a space and a number indicating its type. The separator between field names0and the individual columns is a comma and a space. You may find it more useful to use getline(istream&, String&, char) here because the third parameter describes a0delimiter and second paramter is populated with data until that delimiter.
~Table();
Deallocate all of the fields in the Table object. Remember to deallocate the0dynamic array.
void addRecord(int index, String *record);
Adds a record to the provided row index of the table, record is an0array containing the values of every field (same order as the fields themselves).
COMP 2012H Assignment 6: Simple Relational Database Management System
Null pointer:
If record is a null pointer, print Record is empty.\n and0return.
record is empty ("") or is the0same as another record, print Empty and0return.
INT but the corresponding value in0record is not an integer,0print Cannot
insert non-integer in integer field.\n and return.
Do validation according to the order above. For example, if there are both dimension mismatch and primary key error, you print the message of dimension mismatch and return.
Procedure:
If the new record will not be the last row, do some moving to make space for the0new record. The relative order of those original records should remain the same. Insert the data from the record and increase numRows.
If numRows is equal to tableSize then:
Allocate a new dynamic array with 100 more positions and update0tableSize.
Copy all the data from the old column to the new column.
Deallocate the old column and make the current field point to the new0column.
You may use the == operator to compare two String-type objects. Additionally,0you may use the provided0isInteger(const String&) function to check whether a particular0String object is a number or not.
void addField(int index, const String &name, TYPE type);
Allocate a new Field struct and place it at position index of0the linked list.
Dimensions mismatch:
If the target index is greater than numCols or smaller0than 0, print Table column index is out of bounds.\n and return.
Name error:
If a field with the given name already exists in the table, print0Field with given name already exists in table.\n and return.
Again, do validation according to the order above.
Procedure
Allocate a new field. Additionally, allocate a String array and make0Field::column point to it. Increase numCols.
Place the field at the appropriate position in the linked-list. For instance, if the0the target index is 1, set the new field's index to be the new second (index 1)0column.
If the field is of type INT, initialise all cells to "0",0otherwise initialize them to "".
Additionally, if this the first field to be added to the table, set it to be the0primary key.
void deleteRecord(int row);
Delete a record from the table.
If row is greater than or equal to the number of rows in the table or0smaller than 0, print Table row index is out of bounds.\n and return.
Otherwise, delete the row indicated by row. This is done by remove the0corresponding value in every field. If the deleted row is not the last row, do some0moving0to fill the blank. The relative order of the other records should remain the same. Decrease numRows.
void modifyRecord(int row, int column, const String &newVal);
Modify the cell specified by row and column to hold0newVal.
Dimensions mismatch:
is greater than or equal to numRows or less than 0,0print0Table row index is out of bounds.\n and return.
If column is greater than or equal to numCols or less than00, print0Table column index is out
and return.
Primary key error:
If this modification is going to trigger a primary key conflict or if the primary0key0is going to be empty (""),0print Empty or duplicate primary key.\n and return.
Type mismatch:
If strVal is a string but the column that it corresponds to is of type0INT,0print Cannot insert
non-integer in integer field.\n and return.
Again, do validation according to the order above.
Again, you may use the provided0isInteger(const String&) function to check whether a String
void setPrimaryKey(const String &name);
Set the field specified by name as the new primary key.
Errors:
If there is no field with the name name exists.\n and return.
If there is such a field, check the column.
If the column has any duplicates, print0Cannot set field with duplicate elements as primary key.\n and0return.
If the column has any empty data (""), print0Cannot set field with empty data as primary key.\n and return.
void setColumnIndex(int index, const String &target);
Move the field specified by target so that it is at position0index.
If no field with the given name exists in the table, print0No such field with provided name exists.\n and return.
If index is greater than or equal to numCols or less than00, print0Table column index is out of bounds.\n and return.
Again, do validation according to the order above.
void deleteField(const String &name);
Delete the field specified by name.
If no field with the given name exists in the table, print0No such field with provided name exists.\n and return.
If the target field to be deleted is the primary key, print0Cannot delete primary key field.\n and return.
Otherwise, deallocate the field and the dynamic array, decrease0numCols.
Field* findField(const String &name) const;
Return a pointer to the field specified by name.
If no field with the given name exists in the table, print0No such field with provided name exists.\n and return0nullptr.
void sortTable(Field* field);
Sorts the rows by field in an ascending order. If the field has type0INT0then ascending order should be numeric. Otherwise, the ascending order should be alphabetic.
If field is a null pointer, print0Invalid field provided.\n and0return.
Otherwise you may assume this field exists in the table and you can0start sorting.
For String objects you may use the comparison operators0>, <, >=, <= to compare two Strings. These operators check for alphabetical0orders. For integers, comparisons must b done after converting them to integers. This can0be achieved by using the stoi() function that returns the numeric value of the0number held in the String object.0You may assume that the field to be sorted will not contain duplicate values.
End of Part 2