Общее описание задачи на C++

от вас требуется реализовать собственную in-memory реляционную базу данных.
Чтобы не беспокоиться о сетевых взаимодействиях, правах доступа, пользовательском интерфейсе и прочих подобных вещах, оформить ее предлагается в виде библиотеки, которую можно подключить к произвольному проекту и использовать либо напрямую из пользовательского кода, либо через сторонние интерфейсы (по сети, из консоли или как-нибудь еще).
Что представляет из себя база данных? На самом деле, просто набор таблиц, состоящих из именованных колонок (как правило, их число фиксировано, либо меняется очень редко) и произвольного количества строк (вот они могут добавляться и удаляться часто). Можно думать об этом, как о множестве неких структур.
Естественно, просто хранить таблицы недостатчоно, требуется так же выполнять запросы к ним. Запросы как правило заключаются в добавлении /удалении строк, изменениии существующих и выборке по произвольным условиям. Естественно, критична производительность всего этого.
Требуются следующие возможности:
1. Создание объекта базы. Независимых объектов может быть много и с точки зрения пользователя они ничего не знают друг о друге.
2. Выполнение запросов (иначе какая это база данных). Поскольку предполагается, что запросы могут поступать динамически, требуется единый интерфейс, принимающий их в текстовом виде. Условный db.execute(user_query). О языке запросов - в секции 3.
3. Получение результатов запросов. Если точнее, хочется иметь возможность проитерироваться по строкам результата и получить из них значения конкретных колонок по их именам.
4. Сохранение состояний базы в файл и последующая его загрузка.
5. Естественно, критичны скорость работы и потребление памяти.

Ожидаемый интерфейс выглядит примерно так (но вы можете придумать лучше):
```
memdb::Database db;
db.load_from_file(std::ifstream("db.bin", ios::bin));
auto result = db.execute(query);
if (result.is_ok()) {
for (auto &row : result) {
int i = resp.get<int>("column1");
std::string_view s = resp.get<std::string_view>("column2");
do_something(i, s);
}
} else {
std::cerr << "Error: " << result.get_error() << "\n";
}
...
db.save_to_file(std::ofstream("db.bin", ios::bin));
```

Способ сдачи и требования к решению

Проверка проходит вручную. На проверку вы сдаете код с описанием его работы и инструкцией по компиляции и использованию. 
Запрещено использование библиотек, которые уже реализуют требуемую функциональность или ее часть (например LLVM или sqlite).
Служебные библиотеки (например ranges) или библиотеки для тестирования (boost::ut, gtest) — разрешены.
К реализации должны быть написаны тесты.
Проверяющий в праве задать любые уточняющие вопросы.
Не забывайте про хорошую декомпозицию кода.

Язык запросов

Можно было бы реализовать полноценный SQL, но мы возьмем собственный язык, похожий на него. Это во-первых, позволит проигнорировать несущественные вещи из SQL, а во-вторых даст возможность для расширений языка.

Общие правила:

- Ключевые слова (select, create и т.п.) - нечувствительны к регистру.
- Названия пользовательских сущностей (таблиц и колонок) — чувствительны.
- Требования к названиям — такие же, как в С++. Для простоты считаем, что все символы — ASCI.
- Пробельные символы (включая перевод строки) служат для разделения ключевых слов или параметров. Разделитель может содержать произвольное их количество (но, конечно, больше нуля).

Типы данных

Для простоты введем всего 4 типа данных:

1. int32 — знаковое 32-битное целое число. В запросах записывается, как последовательность цифр, возможно начинающаяся со знака + или -.
2. bool — логическое значение (true или false). В запросах записывается одноименными ключевыми словами.
3. string[X] - строка длины не больше X. В запросах записывается внутри двойных ковычек ("). Может содержать экранированные символы, аналогично строковым литералам в С++ (например "\x00\n\t").
4. bytes[X] - байтовая последовательность длины ровно X. В запросах описывается либо аналогично строке, либо в шестнадцатиричном виде: 0x000abeef

Выражения

Чтобы делать любые хоть немного сложные запросы надо уметь выполнять операции с данными. Мы ограничимся следующими:

- Арифметика (+, -, *, /, %) - выполняется над числами, семантика аналогична С++.
- Сравнения (<, =, >, <=, >=, !=) - выполняется над двумя значениями одинакового типа, семантика аналогична С++, кроме операции =, которая аналогична ==. Числа сравниваются очевидным образом, строки и байтовые последовательности - лексикографически, логические значения - по правилу true > false.
- Логические операции &&, ||, !, ^^ - применяются к логическим значениям, аналогичны С++, кроме ^^, который аналогичен ^
- |s| — применяется к строкам и байтовым последовательностям означет длину строки/последовательности.
- + для строк — конкатенация
- Скобки — задают приоритет операций
Приоритеты операций аналогичны таковым в С++. Неявных преобразваний типов нет, т.е. 42 || 0 или true + false - считаются невалидными выражениями.

Create table

Основная сущность базы данных - это таблица. Но чтобы с ней работать, надо ее сначала создать: create table <имя таблицы> (описание
колонок)
Описание колонок - это последовательность разделенных запятой описаний следующего вида: [{attributes}] <name>: <type> [= <value>],
где
- name - имя колонки
- type - тип значений в колонке
- value - значение по-умалчанию для содержимого колонки, если оно есть
- attributes - последовательность аттрибутов колонки, перечисленных через запятую. Аттрибуты служат для более точного описания свойств колонки. Нужно реализовать хотя бы 3 аттрибута:
+ unique - означает, что значения в колонке должны быть уникальными
+ autoincrement - означет, что при добавлении новых строк значение в этой колонке в первый раз будет 0 и будет становиться на 1 больше в каждой новой строке. Применимо только к целочисленным колонкам.
+ key - то же, что unique, но автоматически построит индекс (о них ниже) по каждой колонке с этим аттрибутом.
Пример запроса: create table users ({key, autoincrement} id : int32, {unique} login: string[32], password_hash: bytes [8], is_admin: bool = false)

Insert

Служит для добавления новых строк в таблицу.
Синтаксис: insert (<values>) to <table>, где
- table - имя таблицы, куда надо вставить значения.
- values - одно из двух. Либо просто список значений через запятую, либо последовательность записей вида <name> = <value>, разделенных запятыми, где name - имя колонки в таблице, a value значение. В случае использования второго вида порядок колонок не важен. Колонки, которые имеют значение по умолчанию или атрибут autoincrement - можно опустить. В первой форме записи для этого просто оставляют пустое место между запятыми (см. примеры).
Примеры:
- insert (,"vasya", Oxdeadbeefdeadbeef) to users
- insert (login = "vasya", password_hash = Oxdeadbeefdeadbeef) to users (эквивалентно предыдущему)
- insert (, "admin", 0x0000000000000000, true) to users
- insert (
    is_admin = true,
    login = "admin",
    password_hash = 0x0000000000000000
) to users (эквивалентно предыдущему)

Select

Запрос выборки данных. Берет таблицу, выбирает из нее данные, соответствующие условия и возвращает новую таблицу.
Синтаксис: select <columns> from <table> where <condition>, где
- table - либо имя таблицы, из которой надо получить значения, либо выражение join (о них ниже)
- columns имена колонок через запятую в формате <table>.<column> (например users.id). Если в запросе используется только одна таблица то ее имя можно опустить и писать только имя колонки.
- condition - логическое выражение над значениями колонок, по которому надо отфильтровать результаты.
Пример: select id, login from users where is_admin || id < 10

Update
Запрос обновления данных.
Синтаксис: update <table> set <assignments> where <condition>,
где
- table - аналогично select
- condition аналогично select
- assignments набор имен колонок и их значений, синтаксис анналогичен второй форме insert за исключением того, что значения могут быть не константами, а выражениями от старых значений данной строки.
Примеры:
- update users set is_admin = true where login = "vasya"
- update users set login = login + "_deleted", is_admin = false where password_hash < 0x00000000ffffffff

Delete
Запрос удаления данных.
Синтаксис: delete <table> where <condition>, где
- table - имя таблицы, из которой надо удалить строки
- condition - аналогично select
Пример:
delete users where |login| % 2 = 0

Join
Естественно, делать запросы к отдельным таблицам - не особо полезно.
Чтобы собирать данные из нескольких таблиц служит join.
Это не отдельная команда, а конструкция, которая может использоваться в запроcax select и update вместо имени таблицы.
join делает очень простую вещь - он выбирает из первой и второй таблицы пары строк, подходящие под условие и составляет новую таблицу, имеющую колонки из обеих предыдущих и содержащую в соответствующих колонках данные из них.
Синтаксис: <table1> join <table2> on <condition>, где
- table1 и table2 - имена таблиц для объединения.
- condition - выражение, определяющее, какие пары записей брать.
Пример: пусть есть 2 таблицы users и posts:
id | login   | is_admin
1  | "vasya" | false
2  | "petya" | false
3  | "admin" | true
и
id | user_id | text
1  | 1       | "А"
2  | 1       | "B"
3  | 3       | "С"
Тoгдa select posts.id, users.login, posts.text from users join posts on users.id = posts.user_id where true
Должен вернуть
id | login   | text
1  | "vasya" | "A"
2  | "vasya" | "B"
3  | "admin" | "C"

Create index
Запрос создания индекса. Индексы никак не влияют на результат запросов, но могут менять время исполнения. Индекс создается по нескольким колонкам и позволяет базе в дальнейшем быстрее выполнять сравнения этих колонок. В нашей базе будет 2 вида индексов:
1. ordered - если такой был создан по колонке х то он позволяет быстро делать выборку, сравнивая значения этой колонки с чем-то. Например select … where x > 10. Естественно, ожидается, что это повлияет и на более сложные запросы с дополнительными условиями, например select … where x > 100 && x < 200 && y > x.
2. unordered создается по набору колонок и позволяет быстро отбирать строки, где весь этот набор равен чему-нибудь. Например, если создать такой индекс по колонкам х и у то ожидается, что запросы типа select … where x = 10 && y = 20 будут выполняться быстро. Аналогично предыдущему, условия могут быть и более сложными.
Мы будем считать, что индексы работают достаточно хорошо, если:

- для ordered - при наличии индекса по колонке х будет наблюдаться заметное ускорение при запросах с условиями вида (…) && (х < 1000) && (x > 500) && (…) (Естественно, порядок выражений может быть произвольным, вместо < и > могут идти другие сравнения, а вместо 1000 и 500 - другие значения, не обязательно числовые).
- для unordered - при наличии индекса по колонкам х, y, z будет наблюдаться заметное ускорение при запросах с условиями вида (…) && (x = 1) && (у = 2) && (z = 3) && (…). (Порядок выражений произвольный, значения могут быть другими).

Если в запросе можно применить несколько индексов то достаточно, чтобы был применен хотя бы один.
Hint: Возможно, тут пригодится STL…
Синтаксис: create <index_type> index on <table> by <columns>, где
- index_type - тип индекса
- table - имя таблицы
- columns - список колонок через запятую.
Примеры:
create ordered index on users by login
create unordered index on users by is_admin

Для всего этого задания можно добавить любые улучшения сверх описанного. Подойдут подзапросы, nullable колонки и 3 вида outer join, constexpr-запросы, ORM, structured binding на результат выборки, новые хитрые виды индексов и атрибутов, особо хорошая оптимизация и все, что может впечатлить проверяющего. Если ты что-то добавляешь, чего не было указано в задании - напиши об этом подробно, в коде сделай дополнительные пометки.

Весь функционал должен быть протестирован. Полно и чётко. За отсутствие тестов или неполное тестирование штрафы.

в настоящий момент структура проекта выглядит так:
memdb_project/
├── include/
│   └── memdb/
│       ├── core/
│       │   ├── Column.h
│       │   ├── Database.h
│       │   ├── DataType.h
│       │   ├── QueryExecutor.h
│       │   ├── QueryParser.h
│       │   ├── QueryResult.h
│       │   ├── Row.h
│       │   ├── RowID.h
│       │   ├── Table.h
│       │   ├── Value.h
│       │   └── exceptions/
│       │       └── DatabaseException.h
│       ├── dependencies/
│       │   └── json.hpp # nlohmann/json
│       └── api/
├── src/
│   └── core/
│       ├── Column.cpp
│       ├── Database.cpp
│       ├── DataType.cpp
│       ├── QueryExecutor.cpp
│       ├── QueryParser.cpp
│       ├── QueryResult.cpp
│       ├── Row.cpp
│       ├── Table.cpp
│       └── Value.cpp
├── examples/
│   └── example_basic_usage.cpp
├── tests/
├── CMakeLists.txt
├── README.md
└── .gitignore

Column.h
```
#ifndef MEMDB_CORE_COLUMN_H
#define MEMDB_CORE_COLUMN_H

#include "memdb/core/DataType.h"
#include "memdb/core/Value.h"
#include <string>
#include <optional>
#include <vector>
#include <stdexcept>

namespace memdb {
namespace core {

// Перечисление атрибутов колонки
enum class ColumnAttribute {
    Unique,
    AutoIncrement,
    Key
};

// Класс для представления колонки таблицы
class Column {
public:
    // Конструкторы
    Column(const std::string& name, const DataType& type,
           const std::vector<ColumnAttribute>& attributes = {},
           const std::optional<Value>& default_value = std::nullopt);

    // Геттеры
    const std::string& get_name() const;
    const DataType& get_type() const;
    const std::vector<ColumnAttribute>& get_attributes() const;
    const std::optional<Value>& get_default_value() const;

    // Проверка наличия атрибута
    bool has_attribute(ColumnAttribute attribute) const;

    // Строковое представление колонки
    std::string to_string() const;

private:
    std::string name_;
    DataType type_;
    std::vector<ColumnAttribute> attributes_;
    std::optional<Value> default_value_;
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_COLUMN_H
```
Database.h
```
#ifndef MEMDB_CORE_DATABASE_H
#define MEMDB_CORE_DATABASE_H

#include "memdb/core/Table.h"
#include "memdb/core/Value.h"
#include "memdb/core/Column.h"
#include "memdb/core/QueryResult.h"
#include "memdb/core/QueryParser.h"
#include "memdb/core/QueryExecutor.h"
#include "memdb/core/exceptions/DatabaseException.h"
#include <string>
#include <unordered_map>
#include <memory>
#include <optional>
#include <vector>
#include <fstream>
#include <stdexcept>

namespace memdb {
namespace core {

// Класс для представления базы данных
class Database {
public:
    Database();

    // Создание таблицы
    void create_table(const std::string& table_name, const std::vector<Column>& columns);

    // Удаление таблицы
    void drop_table(const std::string& table_name);

    // Получение таблицы (для внутреннего использования)
    std::shared_ptr<Table> get_table(const std::string& table_name) const;

    // Проверка существования таблицы
    bool has_table(const std::string& table_name) const;

    // Вставка строки в таблицу
    core::RowID insert_row(const std::string& table_name, const std::vector<std::optional<Value>>& values);

    // Удаление строки из таблицы
    void delete_row(const std::string& table_name, core::RowID row_id);

    // Получение строки из таблицы
    Row& get_row(const std::string& table_name, core::RowID row_id);
    const Row& get_row(const std::string& table_name, core::RowID row_id) const;

    // Получение всех таблиц
    const std::unordered_map<std::string, std::shared_ptr<Table>>& get_all_tables() const;

    // Сохранение базы данных в файл
    void save_to_file(const std::string& filename) const;

    // Загрузка базы данных из файла
    void load_from_file(const std::string& filename);

    // Выполнение запроса
    QueryResult execute(const std::string& query);

    // Строковое представление базы данных
    std::string to_string() const;

private:
    // Хранилище таблиц
    std::unordered_map<std::string, std::shared_ptr<Table>> tables_;

    // Парсер и исполнитель запросов
    QueryParser parser_;
    QueryExecutor executor_;
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_DATABASE_H
```
DataType.h
```
#ifndef MEMDB_CORE_DATATYPE_H
#define MEMDB_CORE_DATATYPE_H

#include "memdb/core/exceptions/TypeMismatchException.h"
#include "memdb/core/enums/Type.h"
#include <string>
#include <variant>
#include <vector>
#include <cstdint>
#include <stdexcept>
#include <iostream>

namespace memdb {
namespace core {

// Класс для представления типа данных с дополнительными параметрами
class DataType {
public:
    // Конструкторы для разных типов
    DataType(Type type);
    DataType(Type type, size_t size); // Для String[X] и Bytes[X]

    // Геттеры
    Type get_type() const;
    size_t get_size() const; // Значение имеет смысл только для String и Bytes

    // Проверка типа
    bool is_string() const;
    bool is_bytes() const;
    bool is_int32() const;
    bool is_int64() const;
    bool is_bool() const;

    // Строковое представление типа
    std::string to_string() const;

private:
    Type type_;
    size_t size_; // Используется только для String[X] и Bytes[X]
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_DATATYPE_H
```
QueryExecutor.h
```
#ifndef MEMDB_CORE_QUERYEXECUTOR_H
#define MEMDB_CORE_QUERYEXECUTOR_H

#include "memdb/core/Database.h"
#include "memdb/core/QueryResult.h"
#include "memdb/core/exceptions/DatabaseException.h"
#include "memdb/core/structs/ParsedQuery.h"

namespace memdb {
namespace core {

class QueryExecutor {
public:
    // Метод для исполнения парсенного запроса
    QueryResult execute(const ParsedQuery& parsed_query, Database& db);
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_QUERYEXECUTOR_H
```
QueryParser.h
```
#ifndef MEMDB_CORE_QUERYPARSER_H
#define MEMDB_CORE_QUERYPARSER_H

#include <string>
#include <memory>
#include "memdb/core/Column.h"
#include "memdb/core/structs/ParsedQuery.h"
#include "memdb/core/QueryResult.h"

namespace memdb {
namespace core {

class QueryParser {
public:
    // Метод для парсинга запроса
    ParsedQuery parse(const std::string& query);
    Value parse_value(const std::string& value_str);

private:
    // Добавляем объявления методов
    Column parse_column_definition(const std::string& col_def);
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_QUERYPARSER_H
```
QueryResult.h
```
#ifndef MEMDB_CORE_QUERYRESULT_H
#define MEMDB_CORE_QUERYRESULT_H

#include "memdb/core/Value.h"
#include <vector>
#include <string>
#include <optional>

namespace memdb {
namespace core {

// Класс для представления результата запроса
class QueryResult {
public:
    // Конструкторы
    QueryResult() : success_(true) {}
    QueryResult(const std::vector<std::vector<std::optional<Value>>>& data)
        : success_(true), data_(data) {}
    QueryResult(const std::string& error_message)
        : success_(false), error_message_(error_message) {}

    // Проверка успешности выполнения запроса
    bool is_ok() const { return success_; }

    // Получение сообщения об ошибке
    std::string get_error() const { return error_message_; }

    // Получение данных результата
    const std::vector<std::vector<std::optional<Value>>>& get_data() const { return data_; }

private:
    bool success_;
    std::string error_message_;
    std::vector<std::vector<std::optional<Value>>> data_;
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_QUERYRESULT_H
```
Row.h
```
#ifndef MEMDB_CORE_ROWID_H
#define MEMDB_CORE_ROWID_H

#include "memdb/core/Value.h"
#include <vector>
#include <optional>
#include <cstdint>
#include <stdexcept>

namespace memdb {
namespace core {

// Тип для идентификации строк в таблице
using RowID = uint64_t;

// Класс для представления строки таблицы
class Row {
public:
    Row(RowID id, const std::vector<std::optional<Value>>& values);

    RowID get_id() const;

    // Удалён метод get_values() const и добавлен неконстантный геттер
    std::vector<std::optional<Value>>& get_values(); // Неконстантный геттер
    const std::vector<std::optional<Value>>& get_values() const; // Константный геттер

    std::optional<Value>& get_value(size_t index);
    const std::optional<Value>& get_value(size_t index) const;

    void set_value(size_t index, const Value& value);

private:
    RowID id_;
    std::vector<std::optional<Value>> values_;
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_ROWID_H
```
RowID.h
```
#ifndef MEMDB_CORE_ROWID_H
#define MEMDB_CORE_ROWID_H

#include <cstdint>

namespace memdb {
namespace core {

// Тип для идентификации строк в таблице
using RowID = uint64_t;

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_ROWID_H
```
Table.h
```
#ifndef MEMDB_CORE_TABLE_H
#define MEMDB_CORE_TABLE_H

#include "memdb/core/Column.h"
#include "memdb/core/DataType.h"
#include "memdb/core/Row.h"
#include "memdb/core/RowID.h"
#include "memdb/core/Value.h"
#include "memdb/core/exceptions/DatabaseException.h"
#include <string>
#include <vector>
#include <unordered_map>
#include <optional>
#include <memory>
#include <stdexcept>
#include <cstdint>
#include <json.hpp> // Добавлено для сериализации


namespace memdb {
namespace core {

// Класс для представления таблицы базы данных
class Table {
public:
    Table(const std::string& name, const std::vector<Column>& columns);

    // Геттеры
    const std::string& get_name() const;
    const std::vector<Column>& get_columns() const;
    size_t get_column_index(const std::string& column_name) const;

    // Операции с колонками
    void add_column(const Column& column);
    bool has_column(const std::string& column_name) const;

    // Операции со строками
    RowID insert_row(const std::vector<std::optional<Value>>& values);
    void delete_row(RowID id);
    Row& get_row(RowID id);
    const Row& get_row(RowID id) const;
    const std::unordered_map<RowID, Row>& get_all_rows() const;

    // Валидация значений при вставке
    void validate_row(const std::vector<std::optional<Value>>& values) const;

    // Методы сериализации и десериализации
    nlohmann::json to_json() const;
    static Table from_json(const nlohmann::json& j);

    // Строковое представление таблицы
    std::string to_string() const;

private:
    std::string name_;
    std::vector<Column> columns_;
    std::unordered_map<RowID, Row> rows_;
    RowID next_row_id_;
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_TABLE_H
```
Value.h
```
#ifndef MEMDB_CORE_VALUE_H
#define MEMDB_CORE_VALUE_H

#include "memdb/core/enums/Type.h"
#include "memdb/core/exceptions/TypeMismatchException.h"
#include <string>
#include <variant>
#include <vector>
#include <cstdint>
#include <optional>
#include <stdexcept>
#include <iostream>

namespace memdb {
namespace core {

// Класс для представления значения ячейки в таблице
class Value {
public:
    // Конструкторы для разных типов
    Value() = default; // Для NULL значений
    Value(int32_t int_val);
    Value(int64_t int_val);
    Value(bool bool_val);
    Value(const std::string& str_val);
    Value(const std::vector<uint8_t>& bytes_val);

    // Геттеры
    Type get_type() const;

    // Возвращает значение или бросает исключение, если тип неверен
    int32_t get_int() const;
    int64_t get_int64() const;
    bool get_bool() const;
    const std::string& get_string() const;
    const std::vector<uint8_t>& get_bytes() const;

    // Сеттеры для изменения значения с проверкой типа
    void set_int(int32_t int_val);
    void set_int64(int64_t int_val);
    void set_bool(bool bool_val);
    void set_string(const std::string& str_val);
    void set_bytes(const std::vector<uint8_t>& bytes_val);

    // Строковое представление значения
    std::string to_string() const;

private:
    std::optional<std::variant<int32_t, int64_t, bool, std::string, std::vector<uint8_t>>> data_;
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_VALUE_H
```
enums/Type.h
```
#ifndef MEMDB_CORE_ENUMS_TYPE_H
#define MEMDB_CORE_ENUMS_TYPE_H

namespace memdb {
namespace core {

enum class Type {
    Int32,
    Int64,
    Bool,
    String,
    Bytes
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_ENUMS_TYPE_H
```
exceptions/DatabaseException.h
```
#ifndef MEMDB_CORE_EXCEPTIONS_DATABASEEXCEPTION_H
#define MEMDB_CORE_EXCEPTIONS_DATABASEEXCEPTION_H

#include <stdexcept>
#include <string>
#include "memdb/core/RowID.h" // Добавлено

namespace memdb {
namespace core {
namespace exceptions {

// Базовый класс для исключений базы данных
class DatabaseException : public std::runtime_error {
public:
    explicit DatabaseException(const std::string& message)
        : std::runtime_error(message) {}
};

// Специфичные исключения можно добавлять здесь
class TableNotFoundException : public DatabaseException {
public:
    explicit TableNotFoundException(const std::string& table_name)
        : DatabaseException("Table not found: " + table_name) {}
};

class TableAlreadyExistsException : public DatabaseException {
public:
    explicit TableAlreadyExistsException(const std::string& table_name)
        : DatabaseException("Table already exists: " + table_name) {}
};

class RowNotFoundException : public DatabaseException {
public:
    explicit RowNotFoundException(core::RowID row_id)
        : DatabaseException("Row ID not found: " + std::to_string(row_id)) {}
};

class SerializationException : public DatabaseException {
public:
    explicit SerializationException(const std::string& message)
        : DatabaseException("Serialization Error: " + message) {}
};

} // namespace exceptions
} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_EXCEPTIONS_DATABASEEXCEPTION_H
```
TypeMismatchException.h
```
#ifndef MEMDB_CORE_EXCEPTIONS_TYPEMISMATCHEXCEPTION_H
#define MEMDB_CORE_EXCEPTIONS_TYPEMISMATCHEXCEPTION_H

#include <stdexcept>
#include <string>

namespace memdb {
namespace core {
namespace exceptions {

class TypeMismatchException : public std::runtime_error {
public:
    explicit TypeMismatchException(const std::string& message)
        : std::runtime_error(message) {}
};

} // namespace exceptions
} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_EXCEPTIONS_TYPEMISMATCHEXCEPTION_H
```
Column.cpp
```
#include "memdb/core/Column.h"

namespace memdb {
namespace core {

// ======= Column Implementation =======

Column::Column(const std::string& name, const DataType& type,
               const std::vector<ColumnAttribute>& attributes,
               const std::optional<Value>& default_value)
    : name_(name), type_(type), attributes_(attributes), default_value_(default_value) {
    // Валидация имени колонки
    if (name.empty()) {
        throw std::invalid_argument("Column name cannot be empty.");
    }

    // Проверка соответствия типа данных и наличия атрибутов
    for (const auto& attr : attributes_) {
        if (attr == ColumnAttribute::AutoIncrement && !type_.is_int32()) {
            throw std::invalid_argument("AutoIncrement attribute can only be applied to int32 columns.");
        }
    }

    // Проверка соответствия типа данных и значения по умолчанию
    if (default_value_) {
        if (default_value_->get_type() != type_.get_type()) {
            throw std::invalid_argument("Default value type does not match column type.");
        }

        // Дополнительные проверки для String и Bytes на длину
        if (type_.is_string()) {
            if (default_value_->get_string().size() > type_.get_size()) {
                throw std::invalid_argument("Default string value exceeds defined size.");
            }
        } else if (type_.is_bytes()) {
            if (default_value_->get_bytes().size() > type_.get_size()) {
                throw std::invalid_argument("Default bytes value exceeds defined size.");
            }
        }
    }
}

const std::string& Column::get_name() const {
    return name_;
}

const DataType& Column::get_type() const {
    return type_;
}

const std::vector<ColumnAttribute>& Column::get_attributes() const {
    return attributes_;
}

const std::optional<Value>& Column::get_default_value() const {
    return default_value_;
}

bool Column::has_attribute(ColumnAttribute attribute) const {
    for (const auto& attr : attributes_) {
        if (attr == attribute) {
            return true;
        }
    }
    return false;
}

std::string Column::to_string() const {
    std::string attr_str;
    if (!attributes_.empty()) {
        attr_str += "{";
        for (size_t i = 0; i < attributes_.size(); ++i) {
            switch (attributes_[i]) {
                case ColumnAttribute::Unique:
                    attr_str += "unique";
                    break;
                case ColumnAttribute::AutoIncrement:
                    attr_str += "autoincrement";
                    break;
                case ColumnAttribute::Key:
                    attr_str += "key";
                    break;
                default:
                    attr_str += "unknown";
            }
            if (i < attributes_.size() - 1) {
                attr_str += ", ";
            }
        }
        attr_str += "} ";
    }

    std::string default_str = "";
    if (default_value_) {
        default_str = " = " + default_value_->to_string();
    }

    return attr_str + name_ + " : " + type_.to_string() + default_str;
}

} // namespace core
} // namespace memdb
```
Database.cpp
```
#include "memdb/core/Database.h"
#include <fstream>
#include <json.hpp> // Рекомендуется использовать для сериализации JSON (добавьте зависимость)
#include "memdb/core/exceptions/DatabaseException.h"

namespace memdb {
namespace core {

using json = nlohmann::json;

// ======= Database Implementation =======

Database::Database() = default;

void Database::create_table(const std::string& table_name, const std::vector<Column>& columns) {
    if (has_table(table_name)) {
        throw exceptions::TableAlreadyExistsException(table_name);
    }
    auto table = std::make_shared<Table>(table_name, columns);
    tables_.emplace(table_name, table);
}

void Database::drop_table(const std::string& table_name) {
    auto it = tables_.find(table_name);
    if (it != tables_.end()) {
        tables_.erase(it);
    } else {
        throw exceptions::TableNotFoundException(table_name);
    }
}

std::shared_ptr<Table> Database::get_table(const std::string& table_name) const {
    auto it = tables_.find(table_name);
    if (it != tables_.end()) {
        return it->second;
    }
    throw exceptions::TableNotFoundException(table_name);
}

bool Database::has_table(const std::string& table_name) const {
    return tables_.find(table_name) != tables_.end();
}

core::RowID Database::insert_row(const std::string& table_name, const std::vector<std::optional<Value>>& values) {
    auto table = get_table(table_name);
    return table->insert_row(values);
}

void Database::delete_row(const std::string& table_name, core::RowID row_id) {
    auto table = get_table(table_name);
    table->delete_row(row_id);
}

Row& Database::get_row(const std::string& table_name, core::RowID row_id) {
    auto table = get_table(table_name);
    return table->get_row(row_id);
}

const Row& Database::get_row(const std::string& table_name, core::RowID row_id) const {
    auto table = get_table(table_name);
    return table->get_row(row_id);
}

const std::unordered_map<std::string, std::shared_ptr<Table>>& Database::get_all_tables() const {
    return tables_;
}

// Корректная сериализация в JSON
void Database::save_to_file(const std::string& filename) const {
    json j;
    j["tables"] = json::array();
    for (const auto& [name, table] : tables_) {
        j["tables"].push_back(table->to_json());
    }
    std::ofstream ofs(filename, std::ios::binary);
    if (!ofs) {
        throw exceptions::SerializationException("Failed to open file for saving: " + filename);
    }
    ofs << j.dump(4); // Форматированный вывод с отступами
}

// Корректная десериализация из JSON
void Database::load_from_file(const std::string& filename) {
    std::ifstream ifs(filename, std::ios::binary);
    if (!ifs) {
        throw exceptions::SerializationException("Failed to open file for loading: " + filename);
    }
    json j;
    ifs >> j;
    if (!j.contains("tables")) {
        throw exceptions::SerializationException("Invalid database file format.");
    }
    for (const auto& table_json : j["tables"]) {
        // Используем метод from_json для создания таблицы
        Table table = Table::from_json(table_json);
        auto table_ptr = std::make_shared<Table>(table);
        tables_.emplace(table.get_name(), table_ptr);
    }
}

std::string Database::to_string() const {
    std::string db_str = "Database:\n";
    for (const auto& [name, table] : tables_) {
        db_str += table->to_string() + "\n";
    }
    return db_str;
}

QueryResult Database::execute(const std::string& query) {
    try {
        auto parsed_query = parser_.parse(query);
        return executor_.execute(parsed_query, *this);
    }
    catch (const std::invalid_argument& e) {
        return QueryResult(e.what());
    }
    catch (const exceptions::DatabaseException& e) {
        return QueryResult(e.what());
    }
    catch (...) {
        return QueryResult("Unknown error during query execution.");
    }
}

} // namespace core
} // namespace memdb
```
DataType.cpp
```
#include "memdb/core/DataType.h"

namespace memdb {
namespace core {

// ======= DataType Implementation =======

DataType::DataType(Type type) : type_(type), size_(0) {
    if ((type_ != Type::String && type_ != Type::Bytes) && size_ != 0) {
        throw std::invalid_argument("Size can only be set for String and Bytes types.");
    }
}

DataType::DataType(Type type, size_t size) : type_(type), size_(size) {
    if ((type_ != Type::String && type_ != Type::Bytes) || size_ == 0) {
        throw std::invalid_argument("Size can only be set for String and Bytes types with size > 0.");
    }
}

Type DataType::get_type() const {
    return type_;
}

size_t DataType::get_size() const {
    if (!is_string() && !is_bytes()) {
        throw std::logic_error("Only String and Bytes types have size.");
    }
    return size_;
}

bool DataType::is_string() const {
    return type_ == Type::String;
}

bool DataType::is_bytes() const {
    return type_ == Type::Bytes;
}

bool DataType::is_int32() const {
    return type_ == Type::Int32;
}

bool DataType::is_int64() const {
    return type_ == Type::Int64;
}

bool DataType::is_bool() const {
    return type_ == Type::Bool;
}

std::string DataType::to_string() const {
    switch (type_) {
        case Type::Int32:
            return "int32";
        case Type::Int64:
            return "int64";
        case Type::Bool:
            return "bool";
        case Type::String:
            return "string[" + std::to_string(size_) + "]";
        case Type::Bytes:
            return "bytes[" + std::to_string(size_) + "]";
        default:
            return "unknown";
    }
}

} // namespace core
} // namespace memdb
```
QueryExecutor.cpp
```
#include "memdb/core/QueryExecutor.h"

#include <algorithm>
#include <iostream>

namespace memdb {
namespace core {

// Метод для исполнения парсенного запроса
QueryResult QueryExecutor::execute(const ParsedQuery& parsed_query, Database& db) {
    switch (parsed_query.type) {
        case ParsedQuery::QueryType::CreateTable:
            try {
                db.create_table(parsed_query.table_name, parsed_query.columns);
                return QueryResult(); // Успех
            }
            catch (const exceptions::DatabaseException& e) {
                return QueryResult(e.what());
            }
            break;
        
        case ParsedQuery::QueryType::Insert:
            try {
                // Получаем таблицу
                std::shared_ptr<Table> table = db.get_table(parsed_query.table_name);
                // Вставляем строку
                core::RowID new_row_id = table->insert_row(*(parsed_query.insert_values));
                // Возвращаем RowID в качестве результата
                std::vector<std::vector<std::optional<Value>>> data = { { 
                    Value(static_cast<int32_t>(new_row_id)) 
                } };
                return QueryResult(data);
            }
            catch (const exceptions::DatabaseException& e) {
                return QueryResult(e.what());
            }
            break;
        
        case ParsedQuery::QueryType::Select:
            // Реализуйте исполнение SELECT
            return QueryResult("SELECT not yet implemented.");
            break;
        
        case ParsedQuery::QueryType::Update:
            // Реализуйте исполнение UPDATE
            return QueryResult("UPDATE not yet implemented.");
            break;
        
        case ParsedQuery::QueryType::Delete:
            // Реализуйте исполнение DELETE
            return QueryResult("DELETE not yet implemented.");
            break;
        
        case ParsedQuery::QueryType::CreateIndex:
            // Реализуйте исполнение CREATE INDEX
            return QueryResult("CREATE INDEX not yet implemented.");
            break;
        
        default:
            return QueryResult("Unsupported query type.");
    }
}

} // namespace core
} // namespace memdb
```
QueryParser.cpp
```
#include "memdb/core/QueryParser.h"
#include <sstream>
#include <algorithm>
#include <cctype>
#include <limits>
#include <stdexcept>
#include <regex>

// Для упрощенного парсинга можно использовать регулярные выражения
// В реальном проекте рекомендуется использовать более надежные методы парсинга

namespace memdb {
namespace core {

ParsedQuery QueryParser::parse(const std::string& query) {
    ParsedQuery pq;
    std::istringstream iss(query);
    std::string command;
    iss >> command;

    // Преобразование команды в нижний регистр для нечувствительности к регистру
    std::transform(command.begin(), command.end(), command.begin(), ::tolower);

    if (command == "create") {
        std::string subcommand;
        iss >> subcommand;
        std::transform(subcommand.begin(), subcommand.end(), subcommand.begin(), ::tolower);
        if (subcommand == "table") {
            pq.type = ParsedQuery::QueryType::CreateTable;
            iss >> pq.table_name;
            char c;
            iss >> c; // Ожидаем '('
            if (c != '(') {
                throw std::invalid_argument("Expected '(' after table name.");
            }

            // Чтение определения колонок до ')'
            std::string columns_def;
            std::getline(iss, columns_def, ')');

            // Разделение колонок по запятой
            std::vector<std::string> column_defs;
            std::stringstream columns_stream(columns_def);
            std::string col_def;
            while (std::getline(columns_stream, col_def, ',')) {
                // Удаление начальных и конечных пробелов
                col_def.erase(col_def.begin(),
                              std::find_if(col_def.begin(), col_def.end(),
                                           [](unsigned char ch) { return !std::isspace(ch); }));
                col_def.erase(std::find_if(col_def.rbegin(), col_def.rend(),
                                           [](unsigned char ch) { return !std::isspace(ch); }).base(),
                              col_def.end());

                column_defs.push_back(col_def);
            }

            // Разбор каждой колонки
            for (const auto& col_def : column_defs) {
                Column column = parse_column_definition(col_def);
                pq.columns.push_back(column);
            }
        }
        else if (subcommand == "index") {
            // Реализуйте парсинг CREATE INDEX
            // Для упрощения пока опустим
            throw std::invalid_argument("CREATE INDEX not yet implemented.");
        }
        else {
            throw std::invalid_argument("Unknown CREATE subcommand: " + subcommand);
        }
    }
    else if (command == "insert") {
        pq.type = ParsedQuery::QueryType::Insert;
        pq.insert_values = std::vector<std::optional<Value>>();
        // Пример синтаксиса:
        // insert (,"vasya", Oxdeadbeefdeadbeef) to users
        // или
        // insert (login = "vasya", password_hash = Oxdeadbeefdeadbeef) to users

        // Используем регулярные выражения для разбора
        std::regex insert_regex(R"(^insert\s*\((.*)\)\s*to\s+(\w+)$)", std::regex::icase);
        std::smatch matches;
        if (std::regex_match(query, matches, insert_regex)) {
            std::string values_str = matches[1];
            std::string table_name = matches[2];
            pq.table_name = table_name;

            // Разделение значений по запятой
            std::vector<std::string> value_defs;
            std::stringstream values_stream(values_str);
            std::string val_def;
            while (std::getline(values_stream, val_def, ',')) {
                // Удаление начальных и конечных пробелов
                val_def.erase(val_def.begin(),
                              std::find_if(val_def.begin(), val_def.end(),
                                           [](unsigned char ch) { return !std::isspace(ch); }));
                val_def.erase(std::find_if(val_def.rbegin(), val_def.rend(),
                                           [](unsigned char ch) { return !std::isspace(ch); }).base(),
                              val_def.end());

                value_defs.push_back(val_def);
            }

            // Парсинг значений
            for (const auto& val_def : value_defs) {
                if (val_def.empty()) {
                    pq.insert_values->emplace_back(std::nullopt); // NULL значение
                }
                else {
                    size_t equal_pos = val_def.find('=');
                    if (equal_pos != std::string::npos) {
                        // Вид: <name> = <value>
                        std::string col_name = val_def.substr(0, equal_pos);
                        std::string value_str = val_def.substr(equal_pos + 1);
                        // Удаление пробелов
                        col_name.erase(col_name.begin(),
                                       std::find_if(col_name.begin(), col_name.end(),
                                                    [](unsigned char ch) { return !std::isspace(ch); }));
                        col_name.erase(std::find_if(col_name.rbegin(), col_name.rend(),
                                                    [](unsigned char ch) { return !std::isspace(ch); }).base(),
                                       col_name.end());

                        value_str.erase(value_str.begin(),
                                       std::find_if(value_str.begin(), value_str.end(),
                                                    [](unsigned char ch) { return !std::isspace(ch); }));
                        value_str.erase(
                            std::find_if(value_str.rbegin(), value_str.rend(),
                                         [](unsigned char ch) { return !std::isspace(ch); })
                                .base(),
                            value_str.end());

                        // Преобразование строки в Value
                        Value value = parse_value(value_str);
                        // Для простоты предполагаем, что порядок колонок совпадает с порядком значений
                        pq.insert_values->push_back(value);
                    }
                    else {
                        // Вид: <value>
                        Value value = parse_value(val_def);
                        pq.insert_values->push_back(value);
                    }
                }
            }
        }
        else {
            throw std::invalid_argument("Invalid INSERT syntax.");
        }
    }
    else if (command == "select") {
        // Реализуйте парсинг SELECT
        // Пока не реализовано
        throw std::invalid_argument("SELECT not yet implemented.");
    }
    else if (command == "update") {
        // Реализуйте парсинг UPDATE
        // Пока не реализовано
        throw std::invalid_argument("UPDATE not yet implemented.");
    }
    else if (command == "delete") {
        // Реализуйте парсинг DELETE
        // Пока не реализовано
        throw std::invalid_argument("DELETE not yet implemented.");
    }
    else {
        throw std::invalid_argument("Unknown command: " + command);
    }

    return pq;
}

Column QueryParser::parse_column_definition(const std::string& col_def) {
    // Формат:
    // [{attributes}] <name> : <type> [= <value>]

    Column column("", DataType(Type::Int32)); // Инициализация с заглушкой

    size_t start = 0;
    // Проверка наличия атрибутов
    if (col_def[start] == '{') {
        size_t end = col_def.find('}', start);
        if (end == std::string::npos) {
            throw std::invalid_argument("Expected '}' for column attributes.");
        }
        std::string attrs_str = col_def.substr(start + 1, end - start - 1);
        std::vector<ColumnAttribute> attributes;
        std::stringstream attrs_stream(attrs_str);
        std::string attr;
        while (std::getline(attrs_stream, attr, ',')) {
            // Удаление пробелов
            attr.erase(attr.begin(),
                      std::find_if(attr.begin(), attr.end(),
                                   [](unsigned char ch) { return !std::isspace(ch); }));
            attr.erase(std::find_if(attr.rbegin(), attr.rend(),
                                    [](unsigned char ch) { return !std::isspace(ch); })
                               .base(),
                       attr.end());

            if (attr == "unique") {
                attributes.emplace_back(ColumnAttribute::Unique);
            }
            else if (attr == "autoincrement") {
                attributes.emplace_back(ColumnAttribute::AutoIncrement);
            }
            else if (attr == "key") {
                attributes.emplace_back(ColumnAttribute::Key);
            }
            else {
                throw std::invalid_argument("Unknown column attribute: " + attr);
            }
        }
        column = Column("", DataType(Type::Int32), attributes);
        start = end + 1;
    }

    // Поиск имени колонки
    size_t colon_pos = col_def.find(':', start);
    if (colon_pos == std::string::npos) {
        throw std::invalid_argument("Expected ':' in column definition.");
    }
    std::string col_name = col_def.substr(start, colon_pos - start);
    // Удаление пробелов
    col_name.erase(col_name.begin(),
                   std::find_if(col_name.begin(), col_name.end(),
                                [](unsigned char ch) { return !std::isspace(ch); }));
    col_name.erase(std::find_if(col_name.rbegin(), col_name.rend(),
                                [](unsigned char ch) { return !std::isspace(ch); })
                       .base(),
                   col_name.end());

    // Поиск типа данных
    size_t type_start = colon_pos + 1;
    // Проверка наличия '=' для значения по умолчанию
    size_t equal_pos = col_def.find('=', type_start);
    std::string type_str;
    std::optional<Value> default_value = std::nullopt;
    if (equal_pos != std::string::npos) {
        type_str = col_def.substr(type_start, equal_pos - type_start);
        // Удаление пробелов
        type_str.erase(type_str.begin(),
                      std::find_if(type_str.begin(), type_str.end(),
                                   [](unsigned char ch) { return !std::isspace(ch); }));
        type_str.erase(
            std::find_if(type_str.rbegin(), type_str.rend(),
                         [](unsigned char ch) { return !std::isspace(ch); })
                .base(),
            type_str.end());

        std::string default_val_str = col_def.substr(equal_pos + 1);
        // Удаление пробелов
        default_val_str.erase(default_val_str.begin(),
                              std::find_if(default_val_str.begin(), default_val_str.end(),
                                           [](unsigned char ch) { return !std::isspace(ch); }));
        default_val_str.erase(
            std::find_if(default_val_str.rbegin(), default_val_str.rend(),
                         [](unsigned char ch) { return !std::isspace(ch); })
                .base(),
            default_val_str.end());

        default_value = parse_value(default_val_str);
    }
    else {
        type_str = col_def.substr(type_start);
        // Удаление пробелов
        type_str.erase(type_str.begin(),
                      std::find_if(type_str.begin(), type_str.end(),
                                   [](unsigned char ch) { return !std::isspace(ch); }));
        type_str.erase(
            std::find_if(type_str.rbegin(), type_str.rend(),
                         [](unsigned char ch) { return !std::isspace(ch); })
                .base(),
            type_str.end());
    }

    // Определение типа данных
    Type type;
    size_t size = 0;
    if (type_str.find("int32") != std::string::npos) {
        type = Type::Int32;
    }
    else if (type_str.find("bool") != std::string::npos) {
        type = Type::Bool;
    }
    else if (type_str.find("string") != std::string::npos) {
        type = Type::String;
        size = std::stoul(type_str.substr(type_str.find('[') + 1,
                                           type_str.find(']') - type_str.find('[') - 1));
    }
    else if (type_str.find("bytes") != std::string::npos) {
        type = Type::Bytes;
        size = std::stoul(type_str.substr(type_str.find('[') + 1,
                                           type_str.find(']') - type_str.find('[') - 1));
    }
    else {
        throw std::invalid_argument("Unknown column type: " + type_str);
    }

    DataType data_type = (type == Type::String || type == Type::Bytes)
                           ? DataType(type, size)
                           : DataType(type);

    // Создание колонки
    if (column.get_name().empty()) {
        column = Column(col_name, data_type, column.get_attributes(), default_value);
    }
    else {
        // Если атрибуты уже заданы
        column = Column(col_name, data_type, column.get_attributes(), default_value);
    }

    return column;
}

Value QueryParser::parse_value(const std::string& value_str) {
    // Определение типа значения по синтаксису
    if (value_str.empty()) {
        throw std::invalid_argument("Empty value.");
    }

    if (value_str[0] == '"' && value_str.back() == '"') {
        // Строка
        std::string str_val = value_str.substr(1, value_str.size() - 2);
        // Можно добавить обработку экранированных символов
        return Value(str_val);
    }
    else if (value_str.find("0x") == 0 || value_str.find("0X") == 0) {
        // Байтовый массив
        std::string hex_str = value_str.substr(2);
        if (hex_str.size() % 2 != 0) {
            throw std::invalid_argument("Invalid hex string for bytes.");
        }
        std::vector<uint8_t> bytes;
        bytes.reserve(hex_str.size() / 2);
        for (size_t i = 0; i < hex_str.size(); i += 2) {
            std::string byte_str = hex_str.substr(i, 2);
            uint8_t byte = static_cast<uint8_t>(std::stoul(byte_str, nullptr, 16));
            bytes.push_back(byte);
        }
        return Value(bytes);
    }
    else if (value_str == "true" || value_str == "false") {
        // Булево значение
        bool bool_val = (value_str == "true");
        return Value(bool_val);
    }
    else {
        // Число (int32 или int64)
        try {
            long long_val = std::stoll(value_str);
            if (long_val >= std::numeric_limits<int32_t>::min() && long_val <= std::numeric_limits<int32_t>::max()) {
                return Value(static_cast<int32_t>(long_val));
            }
            else {
                return Value(static_cast<int64_t>(long_val));
            }
        }
        catch (const std::exception& e) {
            throw std::invalid_argument("Invalid value: " + value_str);
        }
    }
}

} // namespace core
} // namespace memdb
```
QueryResult.cpp
```
#include "memdb/core/QueryResult.h"

// В данном случае реализации нет, так как всё реализовано в заголовочном файле
```
Row.cpp
```
#include "memdb/core/Row.h"
#include "memdb/core/RowID.h"

namespace memdb {
namespace core {

// ======= Row Implementation =======

Row::Row(RowID id, const std::vector<std::optional<Value>>& values)
    : id_(id), values_(values) {}

RowID Row::get_id() const {
    return id_;
}

// Реализация неконстантного геттера
std::vector<std::optional<Value>>& Row::get_values() {
    return values_;
}

// Реализация константного геттера
const std::vector<std::optional<Value>>& Row::get_values() const {
    return values_;
}

std::optional<Value>& Row::get_value(size_t index) {
    if (index >= values_.size()) {
        throw std::out_of_range("Column index out of range.");
    }
    return values_[index];
}

const std::optional<Value>& Row::get_value(size_t index) const {
    if (index >= values_.size()) {
        throw std::out_of_range("Column index out of range.");
    }
    return values_[index];
}

void Row::set_value(size_t index, const Value& value) {
    if (index >= values_.size()) {
        throw std::out_of_range("Column index out of range.");
    }
    values_[index] = value;
}

} // namespace core
} // namespace memdb
```
Table.cpp
```
#include "memdb/core/Row.h"
#include "memdb/core/Table.h"
#include "memdb/core/QueryParser.h"

namespace memdb {
namespace core {

using json = nlohmann::json;

// ======= Table Implementation =======

Table::Table(const std::string& name, const std::vector<Column>& columns)
    : name_(name), columns_(columns), next_row_id_(1) {
    if (name_.empty()) {
        throw std::invalid_argument("Table name cannot be empty.");
    }

    // Проверка уникальности имен колонок
    std::unordered_map<std::string, bool> column_names;
    for (const auto& column : columns_) {
        if (column_names.find(column.get_name()) != column_names.end()) {
            throw std::invalid_argument("Duplicate column name: " + column.get_name());
        }
        column_names[column.get_name()] = true;
    }
}

const std::string& Table::get_name() const {
    return name_;
}

const std::vector<Column>& Table::get_columns() const {
    return columns_;
}

size_t Table::get_column_index(const std::string& column_name) const {
    for (size_t i = 0; i < columns_.size(); ++i) {
        if (columns_[i].get_name() == column_name) {
            return i;
        }
    }
    throw std::invalid_argument("Column not found: " + column_name);
}

void Table::add_column(const Column& column) {
    // Проверка, что колонка с таким именем отсутствует
    if (has_column(column.get_name())) {
        throw std::invalid_argument("Column already exists: " + column.get_name());
    }

    columns_.push_back(column);

    // Добавление нового столбца во все существующие строки с значением по умолчанию или пустым
    for (auto& [id, row] : rows_) {
        if (column.get_default_value()) {
            row.get_values().emplace_back(*(column.get_default_value()));
        }
        else if (column.get_type().is_string()) {
            row.get_values().emplace_back(std::string());
        }
        else if (column.get_type().is_bytes()) {
            row.get_values().emplace_back(std::vector<uint8_t>());
        }
        else if (column.get_type().is_int32()) {
            row.get_values().emplace_back(int32_t(0));
        }
        else if (column.get_type().is_bool()) {
            row.get_values().emplace_back(bool(false));
        }
        else {
            row.get_values().emplace_back(std::nullopt);
        }
    }
}

bool Table::has_column(const std::string& column_name) const {
    for (const auto& column : columns_) {
        if (column.get_name() == column_name) {
            return true;
        }
    }
    return false;
}

RowID Table::insert_row(const std::vector<std::optional<Value>>& values) {
    validate_row(values);

    // Создание полной строки, заполняя отсутствующие значения значениями по умолчанию или autoincrement
    std::vector<std::optional<Value>> complete_values;
    complete_values.reserve(columns_.size());

    for (size_t i = 0; i < columns_.size(); ++i) {
        if (i < values.size() && values[i].has_value()) {
            complete_values.emplace_back(values[i]);
        }
        else {
            const auto& column = columns_[i];
            if (column.has_attribute(ColumnAttribute::AutoIncrement)) {
                // Генерация автонумерованного значения
                complete_values.emplace_back(static_cast<int64_t>(next_row_id_));
            }
            else if (column.get_default_value()) {
                complete_values.emplace_back(*(column.get_default_value()));
            }
            else {
                complete_values.emplace_back(std::nullopt);
            }
        }
    }

    RowID new_id = next_row_id_++;
    rows_.emplace(new_id, Row(new_id, complete_values));
    return new_id;
}

void Table::delete_row(RowID id) {
    auto it = rows_.find(id);
    if (it != rows_.end()) {
        rows_.erase(it);
    }
    else {
        throw std::invalid_argument("Row ID not found: " + std::to_string(id));
    }
}

Row& Table::get_row(RowID id) {
    auto it = rows_.find(id);
    if (it != rows_.end()) {
        return it->second;
    }
    throw std::invalid_argument("Row ID not found: " + std::to_string(id));
}

const Row& Table::get_row(RowID id) const {
    auto it = rows_.find(id);
    if (it != rows_.end()) {
        return it->second;
    }
    throw std::invalid_argument("Row ID not found: " + std::to_string(id));
}

const std::unordered_map<RowID, Row>& Table::get_all_rows() const {
    return rows_;
}

void Table::validate_row(const std::vector<std::optional<Value>>& values) const {
    if (values.size() > columns_.size()) {
        throw std::invalid_argument("Too many values provided for insertion.");
    }

    // Проверка соответствия типов и размеров
    for (size_t i = 0; i < values.size(); ++i) {
        if (!values[i].has_value()) {
            continue; // Пропускаем, значения по умолчанию или NULL
        }

        const auto& value = *(values[i]);
        const auto& column = columns_[i];

        // Проверка соответствия типа данных
        if (value.get_type() != column.get_type().get_type()) {
            throw std::invalid_argument("Type mismatch for column '" + column.get_name() +
                                        "'. Expected: " + column.get_type().to_string() +
                                        ", Got: " + value.to_string());
        }

        // Дополнительные проверки для String и Bytes на длину
        if (column.get_type().is_string()) {
            if (value.get_string().size() > column.get_type().get_size()) {
                throw std::invalid_argument("Value for column '" + column.get_name() +
                                            "' exceeds maximum length.");
            }
        }
        else if (column.get_type().is_bytes()) {
            if (value.get_bytes().size() > column.get_type().get_size()) {
                throw std::invalid_argument("Value for column '" + column.get_name() +
                                            "' exceeds maximum byte size.");
            }
        }
    }

    // Проверка уникальности для колонок с атрибутами unique и key
    for (size_t i = 0; i < values.size(); ++i) {
        if (!values[i].has_value()) {
            continue; // NULL значения могут повторяться, если это допустимо
        }

        const auto& column = columns_[i];
        if (column.has_attribute(ColumnAttribute::Unique) || column.has_attribute(ColumnAttribute::Key)) {
            const Value& new_value = *(values[i]);

            for (const auto& [existing_id, existing_row] : rows_) {
                const auto& existing_value_opt = existing_row.get_value(i);
                if (existing_value_opt.has_value()) {
                    const Value& existing_value = *(existing_value_opt);
                    // Сравнение значений
                    if (new_value.get_type() == existing_value.get_type()) {
                        bool equal = false;
                        switch (new_value.get_type()) {
                            case Type::Int32:
                                equal = (new_value.get_int() == existing_value.get_int());
                                break;
                            case Type::Bool:
                                equal = (new_value.get_bool() == existing_value.get_bool());
                                break;
                            case Type::String:
                                equal = (new_value.get_string() == existing_value.get_string());
                                break;
                            case Type::Bytes:
                                equal = (new_value.get_bytes() == existing_value.get_bytes());
                                break;
                            default:
                                break;
                        }
                        if (equal) {
                            throw std::invalid_argument("Duplicate value for unique/key column '" +
                                                        column.get_name() + "'.");
                        }
                    }
                }
            }
        }
    }
}

nlohmann::json Table::to_json() const {
    nlohmann::json j;
    j["name"] = name_;

    // Сериализация колонок
    j["columns"] = nlohmann::json::array();
    for (const auto& column : columns_) {
        nlohmann::json col_json;
        col_json["name"] = column.get_name();
        col_json["type"] = column.get_type().to_string();

        // Сериализация атрибутов
        nlohmann::json attrs = nlohmann::json::array();
        for (const auto& attr : column.get_attributes()) {
            switch (attr) {
                case ColumnAttribute::Unique:
                    attrs.push_back("unique");
                    break;
                case ColumnAttribute::AutoIncrement:
                    attrs.push_back("autoincrement");
                    break;
                case ColumnAttribute::Key:
                    attrs.push_back("key");
                    break;
                default:
                    attrs.push_back("unknown");
            }
        }
        col_json["attributes"] = attrs;

        // Сериализация значения по умолчанию
        if (column.get_default_value()) {
            col_json["default"] = column.get_default_value()->to_string();
        }

        j["columns"].push_back(col_json);
    }

    // Сериализация строк
    j["rows"] = nlohmann::json::array();
    for (const auto& [id, row] : rows_) {
        nlohmann::json row_json;
        row_json["id"] = id;
        row_json["values"] = nlohmann::json::array();
        for (const auto& value : row.get_values()) {
            if (value.has_value()) {
                row_json["values"].push_back(nlohmann::json::parse(value->to_string()));
            }
            else {
                row_json["values"].push_back(nullptr);
            }
        }
        j["rows"].push_back(row_json);
    }

    return j;
}

Table Table::from_json(const nlohmann::json& j) {
    std::string table_name = j.at("name").get<std::string>();
    std::vector<Column> columns;

    for (const auto& col_json : j.at("columns")) {
        std::string col_name = col_json.at("name").get<std::string>();
        std::string col_type_str = col_json.at("type").get<std::string>();

        // Парсинг типа данных
        Type col_type;
        size_t size = 0;
        if (col_type_str.find("int32") != std::string::npos) {
            col_type = Type::Int32;
        }
        else if (col_type_str.find("bool") != std::string::npos) {
            col_type = Type::Bool;
        }
        else if (col_type_str.find("string") != std::string::npos) {
            col_type = Type::String;
            size = std::stoul(col_type_str.substr(col_type_str.find('[') + 1,
                        col_type_str.find(']') - col_type_str.find('[') - 1));
        }
        else if (col_type_str.find("bytes") != std::string::npos) {
            col_type = Type::Bytes;
            size = std::stoul(col_type_str.substr(col_type_str.find('[') + 1,
                        col_type_str.find(']') - col_type_str.find('[') - 1));
        }
        else {
            throw exceptions::SerializationException("Unknown column type: " + col_type_str);
        }

        DataType col_type_obj = (col_type == Type::String || col_type == Type::Bytes)
                                  ? DataType(col_type, size)
                                  : DataType(col_type);

        // Парсинг атрибутов
        std::vector<ColumnAttribute> attributes;
        for (const auto& attr : col_json.at("attributes")) {
            std::string attr_str = attr.get<std::string>();
            if (attr_str == "unique") {
                attributes.emplace_back(ColumnAttribute::Unique);
            }
            else if (attr_str == "autoincrement") {
                attributes.emplace_back(ColumnAttribute::AutoIncrement);
            }
            else if (attr_str == "key") {
                attributes.emplace_back(ColumnAttribute::Key);
            }
            else {
                // Обработка неизвестных атрибутов
            }
        }

        // Парсинг значения по умолчанию
        std::optional<Value> default_value = std::nullopt;
        if (col_json.contains("default")) {
            std::string default_val_str = col_json.at("default").get<std::string>();
            // Используем QueryParser для парсинга значения
            QueryParser temp_parser;
            default_value = temp_parser.parse_value(default_val_str);
        }

        columns.emplace_back(col_name, col_type_obj, attributes, default_value);
    }

    Table table(table_name, columns);

    // Парсинг строк
    for (const auto& row_json : j.at("rows")) {
        core::RowID row_id = row_json.at("id").get<core::RowID>();
        std::vector<std::optional<Value>> values;
        for (const auto& val_json : row_json.at("values")) {
            if (val_json.is_null()) {
                values.emplace_back(std::nullopt);
            }
            else {
                std::string val_str = val_json.dump(); // Не идеально; требуется улучшить
                QueryParser temp_parser;
                Value val = temp_parser.parse_value(val_str);
                values.emplace_back(val);
            }
        }
        table.insert_row(values);
    }

    return table;
}

} // namespace core
} // namespace memdb
```
Value.cpp
```
#include "memdb/core/Value.h"
#include "memdb/core/exceptions/TypeMismatchException.h"



namespace memdb {
namespace core {

// ======= Value Implementation =======

Value::Value(int32_t int_val) 
    : data_(int_val) {}

Value::Value(int64_t int_val) 
    : data_(int_val) {}

Value::Value(bool bool_val) 
    : data_(bool_val) {}

Value::Value(const std::string& str_val) 
    : data_(str_val) {}

Value::Value(const std::vector<uint8_t>& bytes_val) 
    : data_(bytes_val) {}

Type Value::get_type() const {
    if (!data_) {
        throw exceptions::TypeMismatchException("Attempted to get type of NULL Value.");
    }
    return static_cast<Type>(data_->index());
}

int32_t Value::get_int() const {
    if (!data_ || !std::holds_alternative<int32_t>(*data_)) {
        throw exceptions::TypeMismatchException("Value is not of type Int32.");
    }
    return std::get<int32_t>(*data_);
}

int64_t Value::get_int64() const {
    if (!data_ || !std::holds_alternative<int64_t>(*data_)) {
        throw exceptions::TypeMismatchException("Value is not of type Int64.");
    }
    return std::get<int64_t>(*data_);
}

bool Value::get_bool() const {
    if (!data_ || !std::holds_alternative<bool>(*data_)) {
        throw exceptions::TypeMismatchException("Value is not of type Bool.");
    }
    return std::get<bool>(*data_);
}

const std::string& Value::get_string() const {
    if (!data_ || !std::holds_alternative<std::string>(*data_)) {
        throw exceptions::TypeMismatchException("Value is not of type String.");
    }
    return std::get<std::string>(*data_);
}

const std::vector<uint8_t>& Value::get_bytes() const {
    if (!data_ || !std::holds_alternative<std::vector<uint8_t>>(*data_)) {
        throw exceptions::TypeMismatchException("Value is not of type Bytes.");
    }
    return std::get<std::vector<uint8_t>>(*data_);
}

void Value::set_int(int32_t int_val) {
    if (data_ && !std::holds_alternative<int32_t>(*data_)) {
        throw exceptions::TypeMismatchException("Cannot set Int32 on Value of different type.");
    }
    data_ = int_val;
}

void Value::set_int64(int64_t int_val) {
    if (data_ && !std::holds_alternative<int64_t>(*data_)) {
        throw exceptions::TypeMismatchException("Cannot set Int64 on Value of different type.");
    }
    data_ = int_val;
}

void Value::set_bool(bool bool_val) {
    if (data_ && !std::holds_alternative<bool>(*data_)) {
        throw exceptions::TypeMismatchException("Cannot set Bool on Value of different type.");
    }
    data_ = bool_val;
}

void Value::set_string(const std::string& str_val) {
    if (data_ && !std::holds_alternative<std::string>(*data_)) {
        throw exceptions::TypeMismatchException("Cannot set String on Value of different type.");
    }
    data_ = str_val;
}

void Value::set_bytes(const std::vector<uint8_t>& bytes_val) {
    if (data_ && !std::holds_alternative<std::vector<uint8_t>>(*data_)) {
        throw exceptions::TypeMismatchException("Cannot set Bytes on Value of different type.");
    }
    data_ = bytes_val;
}

std::string Value::to_string() const {
    if (!data_) {
        return "NULL";
    }

    switch (data_->index()) {
        case 0: // int32_t
            return std::to_string(std::get<int32_t>(*data_));
        case 1: // int64_t
            return std::to_string(std::get<int64_t>(*data_));
        case 2: // bool
            return std::get<bool>(*data_) ? "true" : "false";
        case 3: // std::string
            return "\"" + std::get<std::string>(*data_) + "\"";
        case 4: // std::vector<uint8_t>
        {
            const auto& bytes = std::get<std::vector<uint8_t>>(*data_);
            std::string hex_str = "0x";
            const char hex_chars[] = "0123456789ABCDEF";
            for (uint8_t byte : bytes) {
                hex_str += hex_chars[(byte >> 4) & 0xF];
                hex_str += hex_chars[byte & 0xF];
            }
            return hex_str;
        }
        default:
            return "unknown";
    }
}

} // namespace core
} // namespace memdb
```
example_basic_usage.cpp
```
#include "memdb/core/Database.h"
#include "memdb/core/exceptions/DatabaseException.h"
#include "memdb/dependencies/json.hpp" // Убедитесь, что путь корректен
#include <iostream>

int main() {
    memdb::core::Database db;

    try {
        // Создание таблицы "users" с использованием строкового запроса
        std::string create_table_query = R"(
            create table users (
                {autoincrement, key} id : int32[10],
                name : string[50],
                active : bool = false
            )
        )";

        memdb::core::QueryResult result = db.execute(create_table_query);
        if (!result.is_ok()) {
            std::cerr << "Error creating table: " << result.get_error() << std::endl;
            return 1;
        }

        std::cout << "Table 'users' created successfully." << std::endl;

        // Вставка новой строки
        std::string insert_query = R"(
            insert ("Alice", true) to users
        )";

        result = db.execute(insert_query);
        if (!result.is_ok()) {
            std::cerr << "Error inserting row: " << result.get_error() << std::endl;
            return 1;
        }

        std::cout << "Inserted new row into 'users'. Row ID: ";
        // Предполагаем, что результат INSERT возвращает RowID как int64_t
        try {
            const auto& data = result.get_data();
            if (!data.empty() && !data[0].empty() && data[0][0].has_value()) {
                // Здесь необходимо привести тип обратно к RowID (int64_t)
                int64_t row_id = data[0][0]->get_int64();
                std::cout << row_id << std::endl;
            }
            else {
                std::cout << "Unknown" << std::endl;
            }
        }
        catch (const std::exception& e) {
            std::cerr << "Error retrieving RowID: " << e.what() << std::endl;
        }

        // Поскольку SELECT еще не реализован, пропустим его или добавим заглушку
        std::cout << "SELECT operation is not yet implemented." << std::endl;

        // Пример сохранения базы данных в файл
        std::string save_filename = "db.bin";
        db.save_to_file(save_filename);
        std::cout << "Database saved to file: " << save_filename << std::endl;

        // Пример загрузки базы данных из файла
        memdb::core::Database loaded_db;
        loaded_db.load_from_file(save_filename);
        std::cout << "Database loaded from file: " << save_filename << std::endl;

        // Проверка содержимого загруженной базы данных (простой вывод)
        std::cout << "Loaded Database Structure:" << std::endl;
        std::cout << loaded_db.to_string() << std::endl;

    }
    catch (const memdb::core::exceptions::DatabaseException& e) {
        std::cerr << "Database Error: " << e.what() << std::endl;
        return 1;
    }
    catch (const std::exception& e) {
        std::cerr << "Standard Exception: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}
```
при запуске следующего CMakeLists.txt
```
cmake_minimum_required(VERSION 3.14) # Для поддержки FetchContent
project(memdb_project)

# Установка стандарта C++
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# Добавление директорий include
include_directories(include)

# Добавление исходных файлов
file(GLOB CORE_SRC "src/core/*.cpp")
file(GLOB API_SRC "src/api/*.cpp")
file(GLOB EXAMPLES_SRC "examples/*.cpp")
file(GLOB TESTS_SRC "tests/*.cpp")

# Создание статической библиотеки memdb_static
add_library(memdb_static STATIC ${CORE_SRC} ${API_SRC})

# Добавление директории с зависимостями (локальная копия json.hpp)
target_include_directories(memdb_static PRIVATE include/memdb/dependencies)

# Добавление директивы include для всех целей, использующих библиотеку
target_include_directories(memdb_static PUBLIC include/memdb/core)

# Создание исполняемого файла для примера
add_executable(example_basic_usage examples/example_basic_usage.cpp)

# Добавление включения директории для примера
target_include_directories(example_basic_usage PRIVATE include/memdb/dependencies)
target_include_directories(example_basic_usage PRIVATE include/memdb/core)

# Связывание исполняемого файла с библиотекой
target_link_libraries(example_basic_usage memdb_static)

# # Добавление зависимостей для тестов (например, Google Test)
# enable_testing()
# find_package(GTest REQUIRED)
# include_directories(${GTEST_INCLUDE_DIRS})

# # Создание исполняемого файла для тестов
# add_executable(run_tests ${TESTS_SRC})

# # Связывание тестового исполняемого файла с библиотекой и GTest
# target_link_libraries(run_tests memdb_static ${GTEST_LIBRARIES} pthread)

# # Добавление тестов
# add_test(NAME ValueTest COMMAND run_tests)
```
я получаю ошибку
```
kirill@kirill-BRN-FXX:~/Desktop/memdb_project/build$ make
[  8%] Building CXX object CMakeFiles/memdb_static.dir/src/core/Database.cpp.o
In file included from /home/kirill/Desktop/memdb_project/include/memdb/core/Database.h:9,
                 from /home/kirill/Desktop/memdb_project/src/core/Database.cpp:1:
/home/kirill/Desktop/memdb_project/include/memdb/core/QueryExecutor.h:15:58: error: ‘Database’ has not been declared
   15 |     QueryResult execute(const ParsedQuery& parsed_query, Database& db);
      |                                                          ^~~~~~~~
/home/kirill/Desktop/memdb_project/src/core/Database.cpp: In member function ‘memdb::core::QueryResult memdb::core::Database::execute(const std::string&)’:
/home/kirill/Desktop/memdb_project/src/core/Database.cpp:112:48: error: cannot convert ‘memdb::core::Database’ to ‘int&’
  112 |         return executor_.execute(parsed_query, *this);
      |                                                ^~~~~
      |                                                |
      |                                                memdb::core::Database
/home/kirill/Desktop/memdb_project/include/memdb/core/QueryExecutor.h:15:68: note:   initializing argument 2 of ‘memdb::core::QueryResult memdb::core::QueryExecutor::execute(const memdb::core::ParsedQuery&, int&)’
   15 |     QueryResult execute(const ParsedQuery& parsed_query, Database& db);
      |                                                          ~~~~~~~~~~^~
make[2]: *** [CMakeFiles/memdb_static.dir/build.make:107: CMakeFiles/memdb_static.dir/src/core/Database.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:90: CMakeFiles/memdb_static.dir/all] Error 2
make: *** [Makefile:91: all] Error 2
```

что не так?