Simple Relational Database Management System (RDBMS) created with C++20. App is intended to serve as a storage, which enable to manage data with query language similar to SQL. User can create and manage multiple databases within the entire system.
Each database is represented by the Database class, which stores essential information such as the database name (std::string) and a collection of tables (std::unordered_map). Each table is modeled using the Table class, which contains:
- Table name (
std::string) - Column names (
std::vector<std::string>) - Column types (
std::vector<ColumnType>) - Column-level constraints (
std::vector<std::vector<Constraint>> - Foreign key definitions (
std::vector<std::pair<Table*, std::string>>) - Each foreign key pair links a local column (by position) to a specific column in another table, ensuring referential integrity. - Stored data (
std::vector<std::string>)
Supported data types include INTEGER, FLOAT, and TEXT, all defined in the ColumnType enum. The system enforces type safety, preventing invalid inserts (e.g., adding text to an integer column). Constraints such as PRIMARY KEY, FOREIGN KEY, UNIQUE, and NOT NULL are also supported and represented via the Constraint enum.
While building this system, I learned how relational databases work under the hood - including how they handle schemas, data types, constraints, and foreign keys. This helped me better understand how higher-level tools work and how to connect application development with how databases are actually built.
- C++20
- CMake
- Possibility to manage multiple independent databases in one environment.
- Support for creating and managing tables with the ability to define columns with precisely defined data types:
INTEGER,FLOAT, andTEXT. - Possibility to add important constraints on columns to ensure data integrity:
PRIMARY KEY,FOREIGN KEY,UNIQUE, andNOT NULL. - Parser, which has ability to interpret queries written in a SQL-like syntax and execute proper operations based on entered query.
- Possibility to save and read RDBMS data from file. Enable to restore the entire RDBMS structure including databases, tables, columns, constraints and data.
Implemented query language is similar to SQL but includes some key differences:
- No semicolon required β Queries do not need to end with a semicolon.
- Case-sensitive syntax β All clauses must be written in uppercase. Lowercase or mixed-case keywords will not be recognized. Example: β DATABASE CREATE db β Database Create db
-
DATABASE CREATE <database_name>
Creates an empty database with no tables.Example:
DATABASE CREATE shop -
DATABASE USE <database_name>
Selects the database to work with.Example:
DATABASE USE shop -
DATABASE DROP <database_name>
Deletes the specified database.Example:
DATABASE DROP shop -
TABLE CREATE <table_name> WITH COLUMNS <column_name>(<type>)[<constraints>]{<foreign_reference>}
Creates a new table with specified columns, types, constraints, and optional foreign key references. Constraints are written without spaces, separated by commas inside square brackets. The foreign key reference (inside{}) is optional and only required when the column should reference another table.Example:
TABLE CREATE watches WITH COLUMNS id(INTEGER)[PRIMARY_KEY], model(TEXT)[NOT_NULL], own_id(INTEGER)[FOREIGN_KEY]{users.id} -
TABLE DROP <table_name>
Removes a table from the current database.Example:
TABLE DROP watches -
ALTER TABLE <table_name> ADD COLUMN <column_name>(<type>)[<constraints>]{<foreign_reference>}
Adds a new column with optional constraints and an optional foreign key reference.Example:
ALTER TABLE users ADD COLUMN id2(INTEGER)[PRIMARY_KEY] -
ALTER TABLE <table_name> DROP COLUMN <column_name>
Removes a column from the table.Example:
ALTER TABLE users DROP COLUMN id2 -
ALTER TABLE <table_name> ADD CONSTRAINT FOREIGN_KEY REFERENCES <column_name>{<referenced_table>.<referenced_column>}
Adds a foreign key constraint to a column, referencing another table.Example:
ALTER TABLE pets ADD CONSTRAINT FOREIGN_KEY REFERENCES own_id{users.id} -
ALTER TABLE <table_name> ADD CONSTRAINT <CONSTRAINT_TYPE> ON <column_name>
Adds a general constraint (PRIMARY_KEY,UNIQUE, orNOT_NULL) to a column.Example:
ALTER TABLE users ADD CONSTRAINT UNIQUE ON pesel
-
INSERT INTO <table_name> VALUES ...
Inserts a new row into the specified table. Commas between values are optional. Multi-word strings can be wrapped in single quotes ('...').Examples:
INSERT INTO users VALUES 1 'Wiktor Chudy'INSERT INTO users VALUES 1, WiktorINSERT INTO users VALUES 1 Wiktor -
UPDATE <table_name> SET <column> = <value>, ... WHERE <condition>
Updates records in a table based on optional filtering conditions. You can update multiple columns by separating them with commas afterSET. TheWHEREclause is optional. If omitted, all rows in the table will be updated. Logical operators supported:&&/AND,||/OR. Logical expressions are evaluated left to right, with no precedence, meaning parentheses are not supported.Example:
UPDATE users SET name = Wiktor, weight = 75 WHERE name = wiktor && weight > 100 -
DELETE FROM <table_name> WHERE <condition>
Deletes rows from the specified table. TheWHEREclause is optional. If omitted, all data in the table will be deleted. Logical operators and evaluation rules are the same as forUPDATE.Example:
DELETE FROM users WHERE id = 1
-
SELECT <column1>, <column2>, ... FROM <table1>, <table2>, ... WHERE <condition>
Retrieves values from specified columns and tables. TheWHEREclause is optional.Examples:
SELECT name, age FROM usersSELECT name, age FROM users, petsSELECT name FROM users WHERE age > 25 -
SELECT <columns> FROM <table1> INNER JOIN <table2> ON <table1.column> = <table2.column> WHERE ...
Returns rows with matching values in both tables based on the specified join condition.WHEREclause is optional.Example:
SELECT users.name, pets.name FROM users INNER JOIN pets ON users.id = pets.owner_id -
SELECT <columns> FROM <table1> LEFT JOIN <table2> ON <table1.column> = <table2.column> WHERE ...
Returns all rows from the left table, and the matched rows from the right table.
Rows from the left table with no match will still appear with empty values.
WHEREclause is optional.Example:
SELECT users.name, pets.name FROM users LEFT JOIN pets ON users.id = pets.owner_id -
SELECT <columns> FROM <table1> RIGHT JOIN <table2> ON <table1.column> = <table2.column> WHERE ...
Returns all rows from the right table, and the matched rows from the left table.
WHEREclause is optional.Example:
SELECT users.name, pets.name FROM users RIGHT JOIN pets ON users.id = pets.owner_id -
SELECT <columns> FROM <table1> FULL JOIN <table2> ON <table1.column> = <table2.column> WHERE ...
Returns all rows from both tables. If no match is found,nullor empty values are returned for the missing side.WHEREclause is optional.Example:
SELECT users.name, pets.name FROM users FULL JOIN pets ON users.id = pets.owner_id
-
SAVE
Saves the current state of all databases to a file. -
EXIT
Exits the application.
