Общее описание задачи на 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 <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/exceptions/DatabaseException.h"
#include "memdb/core/QueryResult.h"
#include "memdb/core/QueryParser.h"
#include "memdb/core/QueryExecutor.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 <string>
#include <variant>
#include <vector>
#include <cstdint>
#include <stdexcept>
#include <iostream>

namespace memdb {
namespace core {

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

// Класс для представления типа данных с дополнительными параметрами
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]
};

// Класс для представления значения ячейки
class Value {
public:
    // Конструкторы для разных типов
    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:
    Type type_;
    std::variant<int32_t, int64_t, bool, std::string, std::vector<uint8_t>> data_;
};

// Исключение для ошибки типов
class TypeMismatchException : public std::runtime_error {
public:
    explicit TypeMismatchException(const std::string& message)
        : std::runtime_error(message) {}
};

} // namespace core
} // namespace memdb

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

#include "memdb/core/QueryParser.h"
#include "memdb/core/Database.h"
#include "memdb/core/QueryResult.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/QueryResult.h"

namespace memdb {
namespace core {

// Определите необходимые структурам для представления парсенных запросов
struct ParsedQuery {
    // Примеры полей для различных типов запросов
    enum class QueryType {
        CreateTable,
        Insert,
        Select,
        Update,
        Delete,
        CreateIndex,
        // Добавьте другие типы запросов по необходимости
    } type;

    // Поля для запроса создания таблицы
    std::string table_name;
    std::vector<Column> columns;

    // Поля для запросов вставки
    std::optional<std::vector<std::optional<Value>>> insert_values;

    // Поля для выборки, обновления, удаления
    // Добавьте соответствующие поля
};

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/DataType.h"
#include <string>
#include <variant>
#include <vector>
#include <cstdint>
#include <optional>
#include <stdexcept>
#include <iostream>

namespace memdb {
namespace core {

// Исключение для ошибок несовпадения типов значений
class TypeMismatchException : public std::runtime_error {
public:
    explicit TypeMismatchException(const std::string& message)
        : std::runtime_error(message) {}
};

// Класс для представления значения ячейки в таблице
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
```
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
```
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 "memdb/core/exceptions/DatabaseException.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"

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 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 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 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 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 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 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 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 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 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 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 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 <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;
}
```
мне необходимо, чтобы ты проверил, что нигде нет логических ошибок, что возможные ошибки обработаны (есть смысл добавить ещё больше обработки ещё большего числа ошибок, предварительно написав эти файлы ошибок мне), что каждый класс имеет свой файл, что созданные классы переиспользуются, а не создаются заново в других файлах.