Общее описание задачи на 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
│       │   ├── Expression.h
│       │   ├── ExpressionParser.h
│       │   ├── Index.h
│       │   ├── Lexer.h
│       │   ├── QueryExecutor.h
│       │   ├── QueryParser.h
│       │   ├── QueryResult.h
│       │   ├── Row.h
│       │   ├── Table.h
│       │   ├── Value.h
│       │   ├── exceptions/
│       │   │	├── DatabaseException.h
│       │   │	└── TypeMismatchException.h
│       │   ├── structs/
│       │   │	├── Token.h
│       │   │	└── ParsedQuery.h
│       │   └── enums/
│       │   │	├── TokenType.h
│       │   │	├── ColumnAttributes.h
│       │   	└── Type.h
│       ├── dependencies/
│       │   └── json.hpp # nlohmann/json
│       └── api/
├── src/
│   └── core/
│       ├── Column.cpp
│       ├── Database.cpp
│       ├── DataType.cpp
│       ├── Expression.cpp
│       ├── ExpressionParser.cpp
│       ├── Index.cpp
│       ├── Lexer.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 "memdb/core/enums/ColumnAttribute.h"

#include <string>
#include <optional>
#include <vector>
#include <stdexcept>

namespace memdb {
namespace core {

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

#include "memdb/core/structs/ParsedQuery.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;

    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);
    
    void create_index(const std::string& table_name, const std::string& index_type, const std::vector<std::string>& columns);

    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/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); // for String[X] and Bytes[X]

    Type get_type() const;
    size_t get_size() const; // for String[X] adn Bytes[X]

    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_; // for String[X] adn Bytes[X]
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_DATATYPE_H
```
Expression.h
```
#ifndef MEMDB_CORE_EXPRESSION_H
#define MEMDB_CORE_EXPRESSION_H

#include "memdb/core/Value.h"

#include <memory>
#include <string>
#include <vector>
#include <unordered_map>

namespace memdb {
namespace core {

class Expression {
public:
    virtual ~Expression() = default;
    virtual Value evaluate(const std::unordered_map<std::string, Value>& row) const = 0;
};

class LiteralExpression : public Expression {
public:
    LiteralExpression(const Value& value);
    Value evaluate(const std::unordered_map<std::string, Value>& row) const override;

private:
    Value value_;
};

class VariableExpression : public Expression {
public:
    VariableExpression(const std::string& name);
    Value evaluate(const std::unordered_map<std::string, Value>& row) const override;

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

private:
    std::string name_;
};

class UnaryExpression : public Expression {
public:
    enum class Operator {
        Not,
        Length
    };

    UnaryExpression(Operator op, std::unique_ptr<Expression> operand);
    Value evaluate(const std::unordered_map<std::string, Value>& row) const override;

private:
    Operator op_;
    std::unique_ptr<Expression> operand_;
};

class BinaryExpression : public Expression {
public:
    enum class Operator {
        Add,
        Subtract,
        Multiply,
        Divide,
        Modulo,
        Less,
        LessEqual,
        Greater,
        GreaterEqual,
        Equal,
        NotEqual,
        And,
        Or,
        Xor
    };

    BinaryExpression(Operator op, std::unique_ptr<Expression> left, std::unique_ptr<Expression> right);
    Value evaluate(const std::unordered_map<std::string, Value>& row) const override;

    Operator get_operator() const { return op_; }
    const Expression* get_left() const { return left_.get(); }
    const Expression* get_right() const { return right_.get(); }

private:
    Operator op_;
    std::unique_ptr<Expression> left_;
    std::unique_ptr<Expression> right_;
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_EXPRESSION_H
```
ExpressionParser.h
```
#ifndef MEMDB_CORE_EXPRESSIONPARSER_H
#define MEMDB_CORE_EXPRESSIONPARSER_H

#include "memdb/core/Expression.h"
#include "memdb/core/Lexer.h"

#include <memory>

namespace memdb {
namespace core {

class ExpressionParser {
public:
    explicit ExpressionParser(const std::string& input);
    std::unique_ptr<Expression> parse_expression();

private:
    std::unique_ptr<Expression> parse_logical_or();
    std::unique_ptr<Expression> parse_logical_and();
    std::unique_ptr<Expression> parse_equality();
    std::unique_ptr<Expression> parse_comparison();
    std::unique_ptr<Expression> parse_term();
    std::unique_ptr<Expression> parse_factor();
    std::unique_ptr<Expression> parse_unary();
    std::unique_ptr<Expression> parse_primary();

    Lexer lexer_;
    Token current_token_;

    void consume(TokenType type, const std::string& error_message);
    bool match(TokenType type, const std::string& value = "");
    void advance();
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_EXPRESSIONPARSER_H
```
Index.h
```
#ifndef MEMDB_CORE_INDEX_H
#define MEMDB_CORE_INDEX_H

#include "memdb/core/Value.h"
#include "memdb/core/Row.h"

#include <vector>
#include <unordered_map>
#include <map>
#include <string>
#include <memory>

namespace memdb {
namespace core {

enum class IndexType {
    Ordered,
    Unordered
};

class Index {
public:
    Index(IndexType type, const std::vector<std::string>& columns);

    IndexType get_type() const { return type_; }
    const std::vector<std::string>& get_columns() const { return columns_; }

    void add_row(RowID row_id, const std::unordered_map<std::string, Value>& row);
    void remove_row(RowID row_id, const std::unordered_map<std::string, Value>& row);

    std::vector<RowID> search_unordered(const std::unordered_map<std::string, Value>& condition) const;
    std::vector<RowID> search_ordered(const std::string& column,
                                      const std::optional<Value>& lower,
                                      bool lower_inclusive,
                                      const std::optional<Value>& upper,
                                      bool upper_inclusive) const;

private:
    IndexType type_;
    std::vector<std::string> columns_;
    
    std::unordered_map<std::string, std::vector<RowID>> unordered_map_;
    std::map<std::string, RowID> ordered_map_;
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_INDEX_H
```
Lexer.h
```
#ifndef MEMDB_CORE_LEXER_H
#define MEMDB_CORE_LEXER_H

#include "memdb/core/structs/Token.h"

#include <string>
#include <vector>
#include <variant>
#include <cctype>

namespace memdb {
namespace core {

class Lexer {
public:
    explicit Lexer(const std::string& input);
    Token get_next_token();

private:
    char peek() const;
    char get_char();
    void skip_whitespace();
    Token identifier();
    Token number();
    Token string_literal();
    Token bytes_literal();
    Token operator_token();

    std::string input_;
    size_t pos_;
};

} // namespace core
} // namespace memdb

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

#include "memdb/core/Expression.h"
#include "memdb/core/QueryResult.h"

#include "memdb/core/structs/ParsedQuery.h"

#include <memory>

namespace memdb {
namespace core {

class Database;

class QueryExecutor {
public:
    QueryResult execute(const ParsedQuery& parsed_query, Database& db);
    
private:
    QueryResult execute_select(const ParsedQuery& pq, Database& db);
    QueryResult execute_select2(const ParsedQuery& pq, Database& db);
    QueryResult execute_select3(const ParsedQuery& pq, Database& db);
    QueryResult execute_update(const ParsedQuery& pq, Database& db);
    QueryResult execute_delete(const ParsedQuery& pq, Database& db);
    QueryResult execute_create_index(const ParsedQuery& pq, Database& db);
};

} // namespace core
} // namespace memdb

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

#include "memdb/core/Column.h"

#include "memdb/core/structs/ParsedQuery.h"

#include <string>
#include <memory>

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);
    std::vector<std::string> split_columns(const std::string& columns_def);
    std::string trim(const std::string& s);
};

} // 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_ROW_H
#define MEMDB_CORE_ROW_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;

    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_ROW_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/Value.h"
#include "memdb/core/Index.h"
#include "memdb/core/Expression.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);
    RowID insert_row(const std::vector<std::optional<Value>>& values, RowID id);
    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;
    std::unordered_map<RowID, Row>& get_all_rows();
    const std::vector<std::unique_ptr<Index>>& get_indexes() const { return indexes_; }

    void add_index(const std::string& index_type_str, const std::vector<std::string>& columns);
    std::vector<RowID> find_rows(const std::unique_ptr<Expression>& condition) 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_;
    std::vector<std::unique_ptr<Index>> indexes_;
    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 <string>
#include <variant>
#include <vector>
#include <cstdint>
#include <optional>
#include <stdexcept>
#include <iostream>

namespace memdb {
namespace core {

class Value {
public:
    Value() = default;
    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;
    bool has_value() 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;

    const std::variant<int32_t, int64_t, bool, std::string, std::vector<uint8_t>>& get_variant() const;
    std::variant<int32_t, int64_t, bool, std::string, std::vector<uint8_t>>& get_variant();

    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/ColumnAttribute.h
```
#ifndef MEMDB_CORE_ENUMS_COLUMNATTRIBUTE_H
#define MEMDB_CORE_ENUMS_COLUMNATTRIBUTE_H

namespace memdb {
namespace core {

enum class ColumnAttribute {
    Unique,
    AutoIncrement,
    Key
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_ENUMS_COLUMNATTRIBUTE_H
```
enums/TokenType.h
```
#ifndef MEMDB_CORE_ENUMS_TOKENTYPE_H
#define MEMDB_CORE_ENUMS_TOKENTYPE_H

namespace memdb {
namespace core {

enum class TokenType {
    Identifier,
    Int32Literal,
    Int64Literal,
    BoolLiteral,
    StringLiteral,
    BytesLiteral,
    Operator,
    LeftParen,
    RightParen,
    EndOfInput
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_ENUMS_TOKENTYPE_H
```
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>

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
```
exceptions/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
```
structs/ParsedQuery.h
```
#ifndef MEMDB_CORE_STRUCTS_PARSEDQUERY_H
#define MEMDB_CORE_STRUCTS_PARSEDQUERY_H

#include "memdb/core/Column.h"
#include "memdb/core/Value.h"
#include "memdb/core/Expression.h"

#include <string>
#include <vector>
#include <optional>
#include <unordered_map>

namespace memdb {
namespace core {

struct ParsedQuery {
    enum class QueryType {
        CreateTable,
        Insert,
        Select,
        Update,
        Delete,
        CreateIndex,
    } type;

    std::string table_name;
    std::string table_alias;
    std::vector<Column> columns;

    std::optional<std::vector<std::optional<Value>>> insert_values;
    std::optional<std::unordered_map<std::string, Value>> insert_named_values;

    std::vector<std::string> select_columns;
    std::unique_ptr<Expression> where_clause;

    std::unordered_map<std::string, std::unique_ptr<Expression>> update_assignments;
    std::unique_ptr<Expression> update_where_clause;

    std::unique_ptr<Expression> delete_where_clause;

    std::string from_table;

    std::string index_type;
    std::vector<std::string> index_columns;

    struct JoinInfo {
        std::string table_name;
        std::string table_alias;
        std::unique_ptr<Expression> join_condition;
    };
    std::vector<JoinInfo> joins;

    // TODO: other fields
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_STRUCTS_PARSEDQUERY_H
```
structs/Token.h
```
#ifndef MEMDB_CORE_STRUCTS_TOKEN_H
#define MEMDB_CORE_STRUCTS_TOKEN_H

#include "memdb/core/enums/TokenType.h"

#include <string>

namespace memdb {
namespace core {

struct Token {
    TokenType type;
    std::string value;
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_STRUCTS_TOKEN_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() || type_.is_int64())) {
            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.");
        }

        // for String and 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 "memdb/core/exceptions/DatabaseException.h"

#include <fstream>
#include <json.hpp>

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_;
}

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);
}

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"]) {
        Table table = Table::from_json(table_json);
        auto table_ptr = std::make_shared<Table>(std::move(table));
        tables_.emplace(table_ptr->get_name(), table_ptr);
    }
}

void Database::create_index(const std::string& table_name, const std::string& index_type, const std::vector<std::string>& columns) {
    auto table = get_table(table_name);
    table->add_index(index_type, columns);
}

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
```
Expression.cpp
```
#include "memdb/core/Expression.h"
#include "memdb/core/exceptions/TypeMismatchException.h"

#include <stdexcept>
#include <cmath>

namespace memdb {
namespace core {

int compare_values(const Value& left_val, const Value& right_val) {
    if (left_val.get_type() != right_val.get_type()) {
        throw exceptions::TypeMismatchException("Comparison requires operands of the same type.");
    }

    switch (left_val.get_type()) {
        case Type::Int32:
            return (left_val.get_int() < right_val.get_int()) ? -1 :
                   (left_val.get_int() > right_val.get_int()) ? 1 : 0;

        case Type::Int64:
            return (left_val.get_int64() < right_val.get_int64()) ? -1 :
                   (left_val.get_int64() > right_val.get_int64()) ? 1 : 0;

        case Type::Bool:
            return (left_val.get_bool() == right_val.get_bool()) ? 0 :
                   (left_val.get_bool() == false) ? -1 : 1;

        case Type::String:
            return left_val.get_string().compare(right_val.get_string());

        case Type::Bytes:
            if (left_val.get_bytes() == right_val.get_bytes()) {
                return 0;
            } else if (left_val.get_bytes() < right_val.get_bytes()) {
                return -1;
            } else {
                return 1;
            }

        default:
            throw std::runtime_error("Unsupported type for comparison operations.");
    }
}

// ======= LiteralExpression Implementation =======
LiteralExpression::LiteralExpression(const Value& value) : value_(value) {}

Value LiteralExpression::evaluate(const std::unordered_map<std::string, Value>& row) const {
    return value_;
}

// ======= VariableExpression Implementation =======
VariableExpression::VariableExpression(const std::string& name) : name_(name) {}

Value VariableExpression::evaluate(const std::unordered_map<std::string, Value>& row) const {
    auto it = row.find(name_);
    if (it == row.end()) {
        throw exceptions::TypeMismatchException("Column not found: " + name_);
    }
    if (!it->second.has_value()) {
        throw exceptions::TypeMismatchException("NULL value for column: " + name_);
    }
    return it->second;
}

// ======= UnaryExpression Implementation =======
UnaryExpression::UnaryExpression(Operator op, std::unique_ptr<Expression> operand)
    : op_(op), operand_(std::move(operand)) {}

Value UnaryExpression::evaluate(const std::unordered_map<std::string, Value>& row) const {
    Value val = operand_->evaluate(row);
    switch (op_) {
        case Operator::Not:
            if (val.get_type() != Type::Bool) {
                throw exceptions::TypeMismatchException("Operator '!' requires Bool type.");
            }
            return Value(!val.get_bool());
        case Operator::Length:
            if (val.get_type() == Type::String) {
                return Value(static_cast<int32_t>(val.get_string().size()));
            } else if (val.get_type() == Type::Bytes) {
                return Value(static_cast<int32_t>(val.get_bytes().size()));
            } else {
                throw exceptions::TypeMismatchException("Operator '|s|' requires String or Bytes type.");
            }
        default:
            throw std::runtime_error("Unknown unary operator.");
    }
}

// ======= BinaryExpression Implementation =======
BinaryExpression::BinaryExpression(Operator op, std::unique_ptr<Expression> left, std::unique_ptr<Expression> right)
    : op_(op), left_(std::move(left)), right_(std::move(right)) {}

Value BinaryExpression::evaluate(const std::unordered_map<std::string, Value>& row) const {
    Value left_val = left_->evaluate(row);
    Value right_val = right_->evaluate(row);

    switch (op_) {
        case Operator::Add:
            if (left_val.get_type() == Type::Int32 && right_val.get_type() == Type::Int32) {
                return Value(left_val.get_int() + right_val.get_int());
            }
            if (left_val.get_type() == Type::Int64 && right_val.get_type() == Type::Int64) {
                return Value(left_val.get_int64() + right_val.get_int64());
            }
            if (left_val.get_type() == Type::String && right_val.get_type() == Type::String) {
                return Value(left_val.get_string() + right_val.get_string());
            }
            throw exceptions::TypeMismatchException("Operator '+' not supported for given types.");

        case Operator::Subtract:
            if (left_val.get_type() == Type::Int32 && right_val.get_type() == Type::Int32) {
                return Value(left_val.get_int() - right_val.get_int());
            }
            if (left_val.get_type() == Type::Int64 && right_val.get_type() == Type::Int64) {
                return Value(left_val.get_int64() - right_val.get_int64());
            }
            throw exceptions::TypeMismatchException("Operator '-' requires numeric types.");

        case Operator::Multiply:
            if (left_val.get_type() == Type::Int32 && right_val.get_type() == Type::Int32) {
                return Value(left_val.get_int() * right_val.get_int());
            }
            if (left_val.get_type() == Type::Int64 && right_val.get_type() == Type::Int64) {
                return Value(left_val.get_int64() * right_val.get_int64());
            }
            throw exceptions::TypeMismatchException("Operator '*' requires numeric types.");

        case Operator::Divide:
            if (left_val.get_type() == Type::Int32 && right_val.get_type() == Type::Int32) {
                if (right_val.get_int() == 0) throw exceptions::TypeMismatchException("Division by zero.");
                return Value(left_val.get_int() / right_val.get_int());
            }
            if (left_val.get_type() == Type::Int64 && right_val.get_type() == Type::Int64) {
                if (right_val.get_int64() == 0) throw exceptions::TypeMismatchException("Division by zero.");
                return Value(left_val.get_int64() / right_val.get_int64());
            }
            throw exceptions::TypeMismatchException("Operator '/' requires numeric types.");

        case Operator::Modulo:
            if (left_val.get_type() == Type::Int32 && right_val.get_type() == Type::Int32) {
                if (right_val.get_int() == 0) throw exceptions::TypeMismatchException("Modulo by zero.");
                return Value(left_val.get_int() % right_val.get_int());
            }
            if (left_val.get_type() == Type::Int64 && right_val.get_type() == Type::Int64) {
                if (right_val.get_int64() == 0) throw exceptions::TypeMismatchException("Modulo by zero.");
                return Value(left_val.get_int64() % right_val.get_int64());
            }
            throw exceptions::TypeMismatchException("Operator '%' requires integer types.");

                case Operator::Less:
            if (left_val.get_type() != right_val.get_type()) {
                throw exceptions::TypeMismatchException("Comparison requires operands of the same type.");
            }
            return Value(compare_values(left_val, right_val) < 0);

        case Operator::LessEqual:
            if (left_val.get_type() != right_val.get_type()) {
                throw exceptions::TypeMismatchException("Comparison requires operands of the same type.");
            }
            return Value(compare_values(left_val, right_val) <= 0);

        case Operator::Greater:
            if (left_val.get_type() != right_val.get_type()) {
                throw exceptions::TypeMismatchException("Comparison requires operands of the same type.");
            }
            return Value(compare_values(left_val, right_val) > 0);

        case Operator::GreaterEqual:
            if (left_val.get_type() != right_val.get_type()) {
                throw exceptions::TypeMismatchException("Comparison requires operands of the same type.");
            }
            return Value(compare_values(left_val, right_val) >= 0);

        case Operator::Equal:
            if (left_val.get_type() != right_val.get_type()) {
                throw exceptions::TypeMismatchException("Equality comparison requires operands of the same type.");
            }
            return Value(compare_values(left_val, right_val) == 0);

        case Operator::NotEqual:
            if (left_val.get_type() != right_val.get_type()) {
                throw exceptions::TypeMismatchException("Inequality comparison requires operands of the same type.");
            }
            return Value(compare_values(left_val, right_val) != 0);

        case Operator::And:
            if (left_val.get_type() != Type::Bool || right_val.get_type() != Type::Bool) {
                throw exceptions::TypeMismatchException("Operator '&&' requires Bool types.");
            }
            return Value(left_val.get_bool() && right_val.get_bool());

        case Operator::Or:
            if (left_val.get_type() != Type::Bool || right_val.get_type() != Type::Bool) {
                throw exceptions::TypeMismatchException("Operator '||' requires Bool types.");
            }
            return Value(left_val.get_bool() || right_val.get_bool());

        case Operator::Xor:
            if (left_val.get_type() != Type::Bool || right_val.get_type() != Type::Bool) {
                throw exceptions::TypeMismatchException("Operator '^^' requires Bool types.");
            }
            return Value(left_val.get_bool() ^ right_val.get_bool());

        default:
            throw std::runtime_error("Unknown binary operator.");
    }
}

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

#include <stdexcept>
#include <limits>
#include <numeric>

namespace memdb {
namespace core {

ExpressionParser::ExpressionParser(const std::string& input) : lexer_(input) {
    advance();
}

void ExpressionParser::advance() {
    current_token_ = lexer_.get_next_token();
}

void ExpressionParser::consume(TokenType type, const std::string& error_message) {
    if (current_token_.type != type) {
        throw exceptions::TypeMismatchException(error_message);
    }
    advance();
}

bool ExpressionParser::match(TokenType type, const std::string& value) {
    if (current_token_.type == type) {
        if (value.empty() || current_token_.value == value) {
            return true;
        }
    }
    return false;
}

std::unique_ptr<Expression> ExpressionParser::parse_expression() {
    return parse_logical_or();
}

std::unique_ptr<Expression> ExpressionParser::parse_logical_or() {
    auto node = parse_logical_and();
    while (match(TokenType::Operator, "||")) {
        std::string op = current_token_.value;
        advance();
        auto right = parse_logical_and();
        node = std::make_unique<BinaryExpression>(BinaryExpression::Operator::Or, std::move(node), std::move(right));
    }
    return node;
}

std::unique_ptr<Expression> ExpressionParser::parse_logical_and() {
    auto node = parse_equality();
    while (match(TokenType::Operator, "&&")) {
        std::string op = current_token_.value;
        advance();
        auto right = parse_equality();
        node = std::make_unique<BinaryExpression>(BinaryExpression::Operator::And, std::move(node), std::move(right));
    }
    return node;
}

std::unique_ptr<Expression> ExpressionParser::parse_equality() {
    auto node = parse_comparison();
    while (match(TokenType::Operator, "==") || match(TokenType::Operator, "!=") || match(TokenType::Operator, "=")) {
        std::string op = current_token_.value;
        advance();
        auto right = parse_comparison();
        if (op == "==" || op == "=") {
            node = std::make_unique<BinaryExpression>(BinaryExpression::Operator::Equal, std::move(node), std::move(right));
        } else if (op == "!=") {
            node = std::make_unique<BinaryExpression>(BinaryExpression::Operator::NotEqual, std::move(node), std::move(right));
        } else {
            throw std::runtime_error("Unknown equality operator: " + op);
        }
    }
    return node;
}

std::unique_ptr<Expression> ExpressionParser::parse_comparison() {
    auto node = parse_term();
    while (match(TokenType::Operator, "<") || match(TokenType::Operator, "<=") ||
           match(TokenType::Operator, ">") || match(TokenType::Operator, ">=")) {
        std::string op = current_token_.value;
        advance();
        auto right = parse_term();
        if (op == "<") {
            node = std::make_unique<BinaryExpression>(BinaryExpression::Operator::Less, std::move(node), std::move(right));
        } else if (op == "<=") {
            node = std::make_unique<BinaryExpression>(BinaryExpression::Operator::LessEqual, std::move(node), std::move(right));
        } else if (op == ">") {
            node = std::make_unique<BinaryExpression>(BinaryExpression::Operator::Greater, std::move(node), std::move(right));
        } else if (op == ">=") {
            node = std::make_unique<BinaryExpression>(BinaryExpression::Operator::GreaterEqual, std::move(node), std::move(right));
        }
    }
    return node;
}

std::unique_ptr<Expression> ExpressionParser::parse_term() {
    auto node = parse_factor();
    while (match(TokenType::Operator, "+") || match(TokenType::Operator, "-")) {
        std::string op = current_token_.value;
        advance();
        auto right = parse_factor();
        if (op == "+") {
            node = std::make_unique<BinaryExpression>(BinaryExpression::Operator::Add, std::move(node), std::move(right));
        } else {
            node = std::make_unique<BinaryExpression>(BinaryExpression::Operator::Subtract, std::move(node), std::move(right));
        }
    }
    return node;
}

std::unique_ptr<Expression> ExpressionParser::parse_factor() {
    auto node = parse_unary();
    while (match(TokenType::Operator, "*") || match(TokenType::Operator, "/") || match(TokenType::Operator, "%")) {
        std::string op = current_token_.value;
        advance();
        auto right = parse_unary();
        if (op == "*") {
            node = std::make_unique<BinaryExpression>(BinaryExpression::Operator::Multiply, std::move(node), std::move(right));
        } else if (op == "/") {
            node = std::make_unique<BinaryExpression>(BinaryExpression::Operator::Divide, std::move(node), std::move(right));
        } else if (op == "%") {
            node = std::make_unique<BinaryExpression>(BinaryExpression::Operator::Modulo, std::move(node), std::move(right));
        }
    }
    return node;
}

std::unique_ptr<Expression> ExpressionParser::parse_unary() {
    if (match(TokenType::Operator, "!")) {
        advance();
        auto operand = parse_unary();
        return std::make_unique<UnaryExpression>(UnaryExpression::Operator::Not, std::move(operand));
    }
    if (match(TokenType::Operator, "|s|")) {
        advance();
        auto operand = parse_unary();
        return std::make_unique<UnaryExpression>(UnaryExpression::Operator::Length, std::move(operand));
    }
    return parse_primary();
}

std::unique_ptr<Expression> ExpressionParser::parse_primary() {
    if (match(TokenType::LeftParen)) {
        advance();
        auto expr = parse_expression();
        consume(TokenType::RightParen, "Expected ')' after expression.");
        return expr;
    }

    if (match(TokenType::Int32Literal) || match(TokenType::Int64Literal)) {
        int64_t value = std::stoll(current_token_.value);
        bool isInt32 = (current_token_.type == TokenType::Int32Literal);
        advance();
        if (isInt32) {
            return std::make_unique<LiteralExpression>(Value(static_cast<int32_t>(value)));
        }
        return std::make_unique<LiteralExpression>(Value(static_cast<int64_t>(value)));
    }

    if (match(TokenType::BoolLiteral)) {
        bool value = (current_token_.value == "true");
        advance();
        return std::make_unique<LiteralExpression>(Value(value));
    }

    if (match(TokenType::StringLiteral)) {
        std::string value = current_token_.value;
        advance();
        return std::make_unique<LiteralExpression>(Value(value));
    }

    if (match(TokenType::BytesLiteral)) {
        std::string hex_str = current_token_.value;
        advance();
        std::vector<uint8_t> bytes;
        if (hex_str.size() < 2 || (hex_str[0] != '0' && hex_str[1] != 'x' && hex_str[1] != 'X')) {
            throw exceptions::TypeMismatchException("Invalid bytes literal.");
        }
        hex_str = hex_str.substr(2);
        if (hex_str.size() % 2 != 0) {
            throw exceptions::TypeMismatchException("Invalid bytes literal length.");
        }
        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::stoi(byte_str, nullptr, 16));
            bytes.push_back(byte);
        }
        return std::make_unique<LiteralExpression>(Value(bytes));
    }

    if (match(TokenType::Identifier)) {
        std::string name = current_token_.value;
        advance();
        return std::make_unique<VariableExpression>(name);
    }

    throw exceptions::TypeMismatchException("Unexpected token in expression.");
}

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

#include "memdb/core/exceptions/DatabaseException.h"

#include <algorithm>

namespace memdb {
namespace core {

// ======= Index Implementation =======

Index::Index(IndexType type, const std::vector<std::string>& columns)
    : type_(type), columns_(columns) {}

void Index::add_row(RowID row_id, const std::unordered_map<std::string, Value>& row) {
    if (type_ == IndexType::Unordered) {
        std::string key;
        for (const auto& col : columns_) {
            if (row.find(col) == row.end()) {
                throw std::invalid_argument("Column '" + col + "' not found in row for index.");
            }
            key += row.at(col).to_string() + "|";
        }
        unordered_map_[key].push_back(row_id);
    }
    else if (type_ == IndexType::Ordered) {
        if (columns_.size() != 1) {
            throw std::invalid_argument("Ordered index can only be created on a single column.");
        }
        const std::string& col = columns_[0];
        if (row.find(col) == row.end()) {
            throw std::invalid_argument("Column '" + col + "' not found in row for index.");
        }
        ordered_map_[row.at(col).to_string()] = row_id;
    }
}

void Index::remove_row(RowID row_id, const std::unordered_map<std::string, Value>& row) {
    if (type_ == IndexType::Unordered) {
        std::string key;
        for (const auto& col : columns_) {
            if (row.find(col) == row.end()) {
                throw std::invalid_argument("Column '" + col + "' not found in row for index.");
            }
            key += row.at(col).to_string() + "|";
        }
        auto it = unordered_map_.find(key);
        if (it != unordered_map_.end()) {
            it->second.erase(std::remove(it->second.begin(), it->second.end(), row_id), it->second.end());
            if (it->second.empty()) {
                unordered_map_.erase(it);
            }
        }
    }
    else if (type_ == IndexType::Ordered) {
        if (columns_.size() != 1) {
            throw std::invalid_argument("Ordered index can only be created on a single column.");
        }
        const std::string& col = columns_[0];
        auto it = ordered_map_.find(row.at(col).to_string());
        if (it != ordered_map_.end() && it->second == row_id) {
            ordered_map_.erase(it);
        }
    }
}

std::vector<RowID> Index::search_unordered(const std::unordered_map<std::string, Value>& condition) const {
    std::vector<RowID> result;
    std::string key;
    for (const auto& col : columns_) {
        auto it = condition.find(col);
        if (it == condition.end()) {
            return result;
        }
        key += it->second.to_string() + "|";
    }
    auto it = unordered_map_.find(key);
    if (it != unordered_map_.end()) {
        result = it->second;
    }
    return result;
}

std::vector<RowID> Index::search_ordered(const std::string& column,
                                        const std::optional<Value>& lower,
                                        bool lower_inclusive,
                                        const std::optional<Value>& upper,
                                        bool upper_inclusive) const {
    std::vector<RowID> result;
    if (columns_.size() != 1 || columns_[0] != column) {
        return result;
    }

    auto it_lower = lower.has_value() ? (lower_inclusive ? ordered_map_.lower_bound(lower->to_string()) 
                                                       : ordered_map_.upper_bound(lower->to_string()))
                                       : ordered_map_.begin();
    auto it_upper = upper.has_value() ? (upper_inclusive ? ordered_map_.upper_bound(upper->to_string()) 
                                                       : ordered_map_.lower_bound(upper->to_string()))
                                       : ordered_map_.end();

    for (auto it = it_lower; it != it_upper; ++it) {
        result.push_back(it->second);
    }

    return result;
}

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

#include "memdb/core/exceptions/TypeMismatchException.h"

#include <limits>
#include <numeric>

namespace memdb {
namespace core {

Lexer::Lexer(const std::string& input) : input_(input), pos_(0) {}

char Lexer::peek() const {
    if (pos_ >= input_.size()) return '\0';
    return input_[pos_];
}

char Lexer::get_char() {
    if (pos_ >= input_.size()) return '\0';
    return input_[pos_++];
}

void Lexer::skip_whitespace() {
    while (std::isspace(peek())) {
        get_char();
    }
}

Token Lexer::get_next_token() {
    skip_whitespace();

    char current = peek();

    if (current == '\0') {
        return Token{TokenType::EndOfInput, ""};
    }

    if (std::isalpha(current) || current == '_') {
        return identifier();
    }

    if (std::isdigit(current) || current == '-' || current == '+') {
        return number();
    }

    if (current == '"') {
        return string_literal();
    }

    if (current == '0' && (input_.size() > pos_ + 1) && (input_[pos_ + 1] == 'x' || input_[pos_ + 1] == 'X')) {
        return bytes_literal();
    }

    switch (current) {
        case '(': get_char(); return Token{TokenType::LeftParen, "("};
        case ')': get_char(); return Token{TokenType::RightParen, ")"};
        case '+': get_char(); return Token{TokenType::Operator, "+"};
        case '-': get_char(); return Token{TokenType::Operator, "-"};
        case '*': get_char(); return Token{TokenType::Operator, "*"};
        case '/': get_char(); return Token{TokenType::Operator, "/"};
        case '%': get_char(); return Token{TokenType::Operator, "%"};
        case '<':
            get_char();
            if (peek() == '=') {
                get_char();
                return Token{TokenType::Operator, "<="};
            }
            return Token{TokenType::Operator, "<"};
        case '>':
            get_char();
            if (peek() == '=') {
                get_char();
                return Token{TokenType::Operator, ">="};
            }
            return Token{TokenType::Operator, ">"};
        case '=':
            get_char();
            if (peek() == '=') {
                get_char();
                return Token{TokenType::Operator, "=="};
            }
            return Token{TokenType::Operator, "="};
        case '!':
            get_char();
            if (peek() == '=') {
                get_char();
                return Token{TokenType::Operator, "!="};
            }
            return Token{TokenType::Operator, "!"};
        case '&':
            get_char();
            if (peek() == '&') {
                get_char();
                return Token{TokenType::Operator, "&&"};
            }
            throw exceptions::TypeMismatchException("Invalid character after '&'");
        case '|':
            get_char();
            if (peek() == '|') {
                get_char();
                return Token{TokenType::Operator, "||"};
            } else if (peek() == 's') { 
                // Special case for |s|
                // Expecting 's|' for |s|
                get_char(); // consume 's'
                if (peek() == '|') {
                    get_char(); // consume '|'
                    return Token{TokenType::Operator, "|s|"};
                }
                throw exceptions::TypeMismatchException("Invalid operator after '|s'");
            }
            return Token{TokenType::Operator, "|"};
        case '^':
            get_char();
            if (peek() == '^') {
                get_char();
                return Token{TokenType::Operator, "^^"};
            }
            throw exceptions::TypeMismatchException("Invalid character after '^'");
        default:
            throw exceptions::TypeMismatchException(std::string("Unknown character: ") + current);
    }
}

Token Lexer::identifier() {
    std::string result;
    while (std::isalnum(peek()) || peek() == '_') {
        result += get_char();
    }

    if (result == "true" || result == "false") {
        return Token{TokenType::BoolLiteral, result};
    }

    return Token{TokenType::Identifier, result};
}

Token Lexer::number() {
    std::string num_str;
    if (peek() == '-' || peek() == '+') {
        num_str += get_char();
    }

    while (std::isdigit(peek())) {
        num_str += get_char();
    }

    long long_val = std::stoll(num_str);
    if (long_val >= std::numeric_limits<int32_t>::min() && long_val <= std::numeric_limits<int32_t>::max()) {
        return Token{TokenType::Int32Literal, num_str};
    } else {
        return Token{TokenType::Int64Literal, num_str};
    }
}

Token Lexer::string_literal() {
    std::string str;
    get_char(); // consume opening ".
    while (peek() != '"' && peek() != '\0') {
        if (peek() == '\\') { 
            // Handle escape sequences
            get_char(); // consume '\\'
            char esc = get_char();
            switch (esc) {
                case 'n': str += '\n'; break;
                case 't': str += '\t'; break;
                case 'r': str += '\r'; break;
                case '\\': str += '\\'; break;
                case '"': str += '"'; break;
                default: str += esc; break;
            }
        } else {
            str += get_char();
        }
    }
    if (peek() == '\0') {
        throw exceptions::TypeMismatchException("Unterminated string literal.");
    }
    get_char(); // consume closing "
    return Token{TokenType::StringLiteral, str};
}

Token Lexer::bytes_literal() {
    std::string bytes_str;
    bytes_str += get_char(); // consume '0'
    bytes_str += get_char(); // consume 'x' or 'X'

    while (std::isxdigit(peek())) {
        bytes_str += get_char();
    }

    return Token{TokenType::BytesLiteral, bytes_str};
}

Token Lexer::operator_token() {
    throw exceptions::TypeMismatchException("Unknown operator.");
}

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

#include "memdb/core/exceptions/DatabaseException.h"
#include "memdb/core/exceptions/TypeMismatchException.h"

#include <algorithm>
#include <iostream>
#include <regex>

namespace memdb {
namespace core {

using json = nlohmann::json;

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());
            }
            catch (const std::exception& e) {
                return QueryResult(e.what());
            }
            catch (...) {
                return QueryResult("Unknown error during query execution.");
            }
            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));
                std::vector<std::vector<std::optional<Value>>> data = { { Value(static_cast<int64_t>(new_row_id)) } };
                return QueryResult(data);
            }
            catch (const exceptions::DatabaseException& e) {
                return QueryResult(e.what());
            }
            break;
        
        case ParsedQuery::QueryType::Select:
            return execute_select3(parsed_query, db);
            break;
        
        case ParsedQuery::QueryType::Update:
            return execute_update(parsed_query, db);
            break;
        
        case ParsedQuery::QueryType::Delete:
            return execute_delete(parsed_query, db);
            break;
        
        case ParsedQuery::QueryType::CreateIndex:
            return execute_create_index(parsed_query, db);
            break;
        
        default:
            return QueryResult("Unsupported query type.");
    }
}

QueryResult QueryExecutor::execute_select(const ParsedQuery& pq, Database& db) {
    try {
        std::shared_ptr<Table> table = db.get_table(pq.table_name);
        const auto& columns = table->get_columns();

        std::vector<std::vector<std::optional<Value>>> results;

        // TODO: use indices for optimization

        for (const auto& [row_id, row] : table->get_all_rows()) {
            std::unordered_map<std::string, Value> row_map;
            for (size_t i = 0; i < columns.size(); ++i) {
                if (row.get_values()[i].has_value()) {
                    row_map[columns[i].get_name()] = *(row.get_values()[i]);
                }
            }

            bool match = true;
            if (pq.where_clause) {
                Value cond = pq.where_clause->evaluate(row_map);
                if (cond.get_type() != Type::Bool) {
                    throw std::invalid_argument("WHERE clause does not evaluate to a boolean.");
                }
                match = cond.get_bool();
            }

            if (match) {
                std::vector<std::optional<Value>> selected_values;
                for (const auto& col_name : pq.select_columns) {
                    size_t dot_pos = col_name.find('.');
                    std::string actual_col_name = col_name;
                    if (dot_pos != std::string::npos) {
                        actual_col_name = col_name.substr(dot_pos + 1);
                    }

                    size_t col_index = table->get_column_index(actual_col_name);
                    if (col_index >= row.get_values().size()) {
                        throw std::invalid_argument("Column '" + actual_col_name + "' does not exist in table '" + pq.table_name + "'.");
                    }
                    selected_values.emplace_back(row.get_values()[col_index]);
                }
                results.emplace_back(selected_values);
            }
        }

        return QueryResult(results);
    }
    catch (const exceptions::DatabaseException& e) {
        return QueryResult(e.what());
    }
}

QueryResult QueryExecutor::execute_select2(const ParsedQuery& pq, Database& db) {
    try {
        std::shared_ptr<Table> table = db.get_table(pq.table_name);
        const auto& columns = table->get_columns();

        std::vector<RowID> candidate_rows;

        bool index_used = false;
        for (const auto& index : table->get_indexes()) {
            if (index->get_type() == IndexType::Unordered && pq.where_clause) {
                // Проверяем, является ли условие простым равенством
                // TODO: Улучшить проверку и поддержку сложных условий

                // Предполагаем, что условие - это бинарное выражение с оператором ==
                BinaryExpression* bin_expr = dynamic_cast<BinaryExpression*>(pq.where_clause.get());
                if (bin_expr && bin_expr->get_operator() == BinaryExpression::Operator::Equal) {
                    const VariableExpression* var_expr = dynamic_cast<const VariableExpression*>(bin_expr->get_left());
                    if (var_expr && index->get_columns().size() == 1 && index->get_columns()[0] == var_expr->get_name()) {
                        // Получаем значение
                        Value val = bin_expr->get_right()->evaluate({});
                        std::unordered_map<std::string, Value> condition_map = { {var_expr->get_name(), val} };
                        candidate_rows = index->search_unordered(condition_map);
                        index_used = true;
                        break;
                    }
                }
            }
        }

        if (!index_used) {
            candidate_rows = table->find_rows(pq.where_clause);
        }

        std::vector<std::vector<std::optional<Value>>> results;

        for (const auto& row_id : candidate_rows) {
            const Row& row = table->get_row(row_id);
            std::unordered_map<std::string, Value> row_map;
            for (size_t i = 0; i < columns.size(); ++i) {
                if (row.get_values()[i].has_value()) {
                    row_map[columns[i].get_name()] = *(row.get_values()[i]);
                }
            }

            bool match = true;
            if (pq.where_clause) {
                Value cond = pq.where_clause->evaluate(row_map);
                if (cond.get_type() != Type::Bool) {
                    throw std::invalid_argument("WHERE clause does not evaluate to a boolean.");
                }
                match = cond.get_bool();
            }

            if (match) {
                std::vector<std::optional<Value>> selected_values;
                for (const auto& col_name : pq.select_columns) {
                    size_t dot_pos = col_name.find('.');
                    std::string actual_col_name = col_name;
                    if (dot_pos != std::string::npos) {
                        actual_col_name = col_name.substr(dot_pos + 1);
                    }

                    size_t col_index = table->get_column_index(actual_col_name);
                    if (col_index >= row.get_values().size()) {
                        throw std::invalid_argument("Column '" + actual_col_name + "' does not exist in table '" + pq.table_name + "'.");
                    }
                    selected_values.emplace_back(row.get_values()[col_index]);
                }
                results.emplace_back(selected_values);
            }
        }

        return QueryResult(results);
    }
    catch (const exceptions::DatabaseException& e) {
        return QueryResult(e.what());
    }
}

QueryResult QueryExecutor::execute_select3(const ParsedQuery& pq, Database& db) {
    try {
        if (!pq.joins.empty()) {
            // Обработка SELECT с JOIN

            // Получаем основные таблицы
            auto table1 = db.get_table(pq.table_name);
            auto table2 = db.get_table(pq.joins[0].table_name); // Предположим, что поддерживается один JOIN

            // Получаем колонки таблиц
            const auto& columns1 = table1->get_columns();
            const auto& columns2 = table2->get_columns();

            // Собираем результат JOIN
            std::vector<std::vector<std::optional<Value>>> results;

            // Проходим по всем строкам первой таблицы
            for (const auto& [row_id1, row1] : table1->get_all_rows()) {
                // Создаём мапу для значений первой строки
                std::unordered_map<std::string, Value> row_map;
                for (size_t i = 0; i < columns1.size(); ++i) {
                    if (row1.get_values()[i].has_value()) {
                        std::string col_name = columns1[i].get_name();
                        row_map[pq.table_name + "." + col_name] = *(row1.get_values()[i]);
                    }
                }

                // Проходим по всем строкам второй таблицы
                for (const auto& [row_id2, row2] : table2->get_all_rows()) {
                    // Добавляем значения второй строки
                    std::unordered_map<std::string, Value> combined_row_map = row_map; // Копируем значения первой строки
                    for (size_t i = 0; i < columns2.size(); ++i) {
                        if (row2.get_values()[i].has_value()) {
                            std::string col_name = columns2[i].get_name();
                            combined_row_map[pq.joins[0].table_name + "." + col_name] = *(row2.get_values()[i]);
                        }
                    }

                    // Проверяем условие JOIN
                    Value join_cond_value = pq.joins[0].join_condition->evaluate(combined_row_map);
                    if (join_cond_value.get_type() != Type::Bool) {
                        throw std::invalid_argument("JOIN condition does not evaluate to a boolean.");
                    }
                    if (join_cond_value.get_bool()) {
                        // Проверяем WHERE-условие, если есть
                        bool where_match = true;
                        if (pq.where_clause) {
                            Value where_cond = pq.where_clause->evaluate(combined_row_map);
                            if (where_cond.get_type() != Type::Bool) {
                                throw std::invalid_argument("WHERE clause does not evaluate to a boolean.");
                            }
                            where_match = where_cond.get_bool();
                        }
                        if (where_match) {
                            // Собираем результаты для SELECT-колонок
                            std::vector<std::optional<Value>> selected_values;
                            for (const auto& col_name : pq.select_columns) {
                                if (combined_row_map.find(col_name) != combined_row_map.end()) {
                                    selected_values.push_back(combined_row_map[col_name]);
                                } else {
                                    throw std::invalid_argument("Column not found: " + col_name);
                                }
                            }
                            results.push_back(selected_values);
                        }
                    }
                }
            }

            return QueryResult(results);
        } else {
            // Обработка SELECT без JOIN
            auto table = db.get_table(pq.table_name);
            const auto& columns = table->get_columns();

            std::vector<std::vector<std::optional<Value>>> results;

            for (const auto& [row_id, row] : table->get_all_rows()) {
                std::unordered_map<std::string, Value> row_map;
                for (size_t i = 0; i < columns.size(); ++i) {
                    if (row.get_values()[i].has_value()) {
                        row_map[columns[i].get_name()] = *(row.get_values()[i]);
                    }
                }

                bool match = true;
                if (pq.where_clause) {
                    Value cond = pq.where_clause->evaluate(row_map);
                    if (cond.get_type() != Type::Bool) {
                        throw std::invalid_argument("WHERE clause does not evaluate to a boolean.");
                    }
                    match = cond.get_bool();
                }

                if (match) {
                    std::vector<std::optional<Value>> selected_values;
                    for (const auto& col_name : pq.select_columns) {
                        if (row_map.find(col_name) != row_map.end()) {
                            selected_values.push_back(row_map[col_name]);
                        } else {
                            throw std::invalid_argument("Column not found: " + col_name);
                        }
                    }
                    results.push_back(selected_values);
                }
            }

            return QueryResult(results);
        }
    } catch (const exceptions::DatabaseException& e) {
        return QueryResult(e.what());
    } catch (const std::exception& e) {
        return QueryResult(e.what());
    }
}

// QueryResult QueryExecutor::execute_select3(const ParsedQuery& pq, Database& db) {
//     try {
//         // Определяем, есть ли JOIN в запросе
//         if (pq.from_table.find("join") != std::string::npos) {
//             // Разбираем JOIN
//             std::string table1_name, table2_name, join_condition;
//             std::regex join_regex(R"((\w+)\s+join\s+(\w+)\s+on\s+(.+))", std::regex::icase);
//             std::smatch join_matches;
//             if (std::regex_match(pq.from_table, join_matches, join_regex)) {
//                 table1_name = join_matches[1].str();
//                 table2_name = join_matches[2].str();
//                 join_condition = join_matches[3].str();
//             } else {
//                 throw std::invalid_argument("Invalid JOIN syntax.");
//             }

//             // Получаем таблицы
//             auto table1 = db.get_table(table1_name);
//             auto table2 = db.get_table(table2_name);

//             // Получаем колонки таблиц
//             const auto& columns1 = table1->get_columns();
//             const auto& columns2 = table2->get_columns();

//             // Собираем результат JOIN
//             std::vector<std::vector<std::optional<Value>>> results;

//             // Создаём выражение для условия JOIN
//             ExpressionParser expr_parser(join_condition);
//             std::unique_ptr<Expression> join_expr = expr_parser.parse_expression();

//             // Проходим по всем строкам первой таблицы
//             for (const auto& [row_id1, row1] : table1->get_all_rows()) {
//                 // Создаём мапу для значений первой строки
//                 std::unordered_map<std::string, Value> row_map1;
//                 for (size_t i = 0; i < columns1.size(); ++i) {
//                     if (row1.get_values()[i].has_value()) {
//                         row_map1[table1_name + "." + columns1[i].get_name()] = *(row1.get_values()[i]);
//                     }
//                 }

//                 // Проходим по всем строкам второй таблицы
//                 for (const auto& [row_id2, row2] : table2->get_all_rows()) {
//                     // Создаём мапу для значений второй строки
//                     std::unordered_map<std::string, Value> row_map2;
//                     for (size_t i = 0; i < columns2.size(); ++i) {
//                         if (row2.get_values()[i].has_value()) {
//                             row_map2[table2_name + "." + columns2[i].get_name()] = *(row2.get_values()[i]);
//                         }
//                     }

//                     // Объединяем мапы
//                     std::unordered_map<std::string, Value> combined_row_map = row_map1;
//                     combined_row_map.insert(row_map2.begin(), row_map2.end());

//                     // Проверяем условие JOIN
//                     Value join_cond_value = join_expr->evaluate(combined_row_map);
//                     if (join_cond_value.get_type() != Type::Bool) {
//                         throw std::invalid_argument("JOIN condition does not evaluate to a boolean.");
//                     }
//                     if (join_cond_value.get_bool()) {
//                         // Проверяем WHERE-условие, если есть
//                         bool where_match = true;
//                         if (pq.where_clause) {
//                             Value where_cond = pq.where_clause->evaluate(combined_row_map);
//                             if (where_cond.get_type() != Type::Bool) {
//                                 throw std::invalid_argument("WHERE clause does not evaluate to a boolean.");
//                             }
//                             where_match = where_cond.get_bool();
//                         }
//                         if (where_match) {
//                             // Собираем результаты для SELECT-колонок
//                             std::vector<std::optional<Value>> selected_values;
//                             for (const auto& col_name : pq.select_columns) {
//                                 if (combined_row_map.find(col_name) != combined_row_map.end()) {
//                                     selected_values.push_back(combined_row_map[col_name]);
//                                 } else {
//                                     throw std::invalid_argument("Column not found: " + col_name);
//                                 }
//                             }
//                             results.push_back(selected_values);
//                         }
//                     }
//                 }
//             }

//             return QueryResult(results);
//         } else {
//             // Обработка обычного SELECT без JOIN
//             // Нужно убедиться, что вы корректно заполняете pq.table_name
//             pq.table_name = pq.from_table; // Если from_table — это просто имя таблицы
//             // Далее используйте существующий код для обработки SELECT-запроса без JOIN
//             // ...
//         }
//     } catch (const exceptions::DatabaseException& e) {
//         return QueryResult(e.what());
//     } catch (const std::exception& e) {
//         return QueryResult(e.what());
//     }
// }

QueryResult QueryExecutor::execute_update(const ParsedQuery& pq, Database& db) {
    try {
        std::shared_ptr<Table> table = db.get_table(pq.table_name);
        const auto& columns = table->get_columns();

        size_t updated_count = 0;

        for (auto& [row_id, row] : table->get_all_rows()) {
            std::unordered_map<std::string, Value> row_map;
            for (size_t i = 0; i < columns.size(); ++i) {
                if (row.get_values()[i].has_value()) {
                    row_map[columns[i].get_name()] = *(row.get_values()[i]);
                }
            }

            bool match = true;
            if (pq.update_where_clause) {
                Value cond = pq.update_where_clause->evaluate(row_map);
                if (cond.get_type() != Type::Bool) {
                    throw std::invalid_argument("WHERE clause does not evaluate to a boolean.");
                }
                match = cond.get_bool();
            }

            if (match) {
                for (const auto& [col_name, expr] : pq.update_assignments) {
                    size_t col_index = table->get_column_index(col_name);
                    const Column& column = columns[col_index];
                    Value new_val = expr->evaluate(row_map);

                    if (new_val.get_type() != column.get_type().get_type()) {
                        throw exceptions::TypeMismatchException("Type mismatch in SET assignment for column '" + col_name + "'.");
                    }

                    row.set_value(col_index, new_val);
                    row_map[col_name] = new_val;
                }
                updated_count++;
            }
        }

        std::vector<std::vector<std::optional<Value>>> data = { { Value(static_cast<int64_t>(updated_count)) } };
        return QueryResult(data);
    }
    catch (const exceptions::DatabaseException& e) {
        return QueryResult(e.what());
    }
}

QueryResult QueryExecutor::execute_delete(const ParsedQuery& pq, Database& db) {
    try {
        std::shared_ptr<Table> table = db.get_table(pq.table_name);
        const auto& columns = table->get_columns();

        std::vector<core::RowID> rows_to_delete;

        for (const auto& [row_id, row] : table->get_all_rows()) {
            std::unordered_map<std::string, Value> row_map;
            for (size_t i = 0; i < columns.size(); ++i) {
                if (row.get_values()[i].has_value()) {
                    row_map[columns[i].get_name()] = *(row.get_values()[i]);
                }
            }

            bool match = true;
            if (pq.delete_where_clause) {
                Value cond = pq.delete_where_clause->evaluate(row_map);
                if (cond.get_type() != Type::Bool) {
                    throw std::invalid_argument("WHERE clause does not evaluate to a boolean.");
                }
                match = cond.get_bool();
            }

            if (match) {
                rows_to_delete.emplace_back(row_id);
            }
        }

        for (const auto& row_id : rows_to_delete) {
            table->delete_row(row_id);
        }

        std::vector<std::vector<std::optional<Value>>> data = { { Value(static_cast<int64_t>(rows_to_delete.size())) } };
        return QueryResult(data);
    }
    catch (const exceptions::DatabaseException& e) {
        return QueryResult(e.what());
    }
}

QueryResult QueryExecutor::execute_create_index(const ParsedQuery& pq, Database& db) {
    try {
        db.create_index(pq.table_name, pq.index_type, pq.index_columns);
        return QueryResult();
    }
    catch (const exceptions::DatabaseException& e) {
        return QueryResult(e.what());
    }
    catch (const std::exception& e) {
        return QueryResult(e.what());
    }
}

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

#include <sstream>
#include <algorithm>
#include <cctype>
#include <limits>
#include <stdexcept>
#include <regex>

namespace memdb {
namespace core {

ParsedQuery QueryParser::parse(const std::string& query) {
    std::string trimmed_query = trim(query);

    ParsedQuery pq;
    std::istringstream iss(trimmed_query);
    std::string command;
    iss >> command;

    std::transform(command.begin(), command.end(), command.begin(), ::tolower);

    if (command == "create") {
        std::string index_type_or_subcommand;
        iss >> index_type_or_subcommand;
        std::transform(index_type_or_subcommand.begin(), index_type_or_subcommand.end(), index_type_or_subcommand.begin(), ::tolower);
        if (index_type_or_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 = split_columns(columns_def);

            for (const auto& col_def : column_defs) {
                std::cout << "Parsing Column Definition: " << col_def << std::endl;
                Column column = parse_column_definition(col_def);
                pq.columns.push_back(column);
            }
        }
        else if (index_type_or_subcommand == "ordered" || index_type_or_subcommand == "unordered") {
            pq.type = ParsedQuery::QueryType::CreateIndex;
            pq.index_type = index_type_or_subcommand;

            std::string index_keyword;
            iss >> index_keyword;
            std::transform(index_keyword.begin(), index_keyword.end(), index_keyword.begin(), ::tolower);
            if (index_keyword != "index") {
                throw std::invalid_argument("Expected 'index' after index type.");
            }

            std::string on_word;
            iss >> on_word;
            std::transform(on_word.begin(), on_word.end(), on_word.begin(), ::tolower);
            if (on_word != "on") {
                throw std::invalid_argument("Expected 'on' after 'create <index_type> index'.");
            }

            iss >> pq.table_name;

            std::string by_word;
            iss >> by_word;
            std::transform(by_word.begin(), by_word.end(), by_word.begin(), ::tolower);
            if (by_word != "by") {
                throw std::invalid_argument("Expected 'by' after table name in 'create index'.");
            }

            std::string columns_str;
            std::getline(iss, columns_str);
            size_t first = columns_str.find_first_not_of(" \t\r\n");
            size_t last = columns_str.find_last_not_of(" \t\r\n");
            if (first != std::string::npos && last != std::string::npos) {
                columns_str = columns_str.substr(first, last - first + 1);
            }

            std::stringstream columns_stream(columns_str);
            std::string col;
            while (std::getline(columns_stream, col, ',')) {
                col.erase(col.begin(), std::find_if(col.begin(), col.end(),
                            [](unsigned char ch) { return !std::isspace(ch); }));
                col.erase(std::find_if(col.rbegin(), col.rend(),
                            [](unsigned char ch) { return !std::isspace(ch); }).base(), col.end());
                pq.index_columns.push_back(col);
            }
        }
        else {
            throw std::invalid_argument("Unknown CREATE subcommand: " + index_type_or_subcommand);
        }
    }
    else if (command == "insert") {
        // insert (,"vasya", Oxdeadbeefdeadbeef) to users
        // insert (login = "vasya", password_hash = Oxdeadbeefdeadbeef) to users
        pq.type = ParsedQuery::QueryType::Insert;
        pq.insert_values = std::vector<std::optional<Value>>();

        std::regex insert_regex(R"(^insert\s*\((.*)\)\s*to\s+(\w+)$)", std::regex::icase);
        std::smatch matches;
        if (std::regex_match(trimmed_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);
                }
                else {
                    size_t equal_pos = val_def.find('=');
                    if (equal_pos != std::string::npos) {
                        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 = parse_value(value_str);
                        pq.insert_values->push_back(value);
                    }
                    else {
                        Value value = parse_value(val_def);
                        pq.insert_values->push_back(value);
                    }
                }
            }
        }
        else {
            throw std::invalid_argument("Invalid INSERT syntax.");
        }
    }
    else if (command == "select") {
        pq.type = ParsedQuery::QueryType::Select;

        std::string rest_of_query;
        std::getline(iss, rest_of_query);
        rest_of_query = command + " " + rest_of_query;

        std::regex select_regex(R"(select\s+(.+?)\s+from\s+(\w+)(?:\s+join\s+(\w+)\s+on\s+(.+?))?(?:\s+where\s+(.+))?$)", std::regex::icase);        
        std::smatch matches;
        if (std::regex_match(rest_of_query, matches, select_regex)) {
            std::string columns_str = matches[1].str();
            std::string table_name = matches[2].str();

            pq.table_name = table_name;

            std::stringstream columns_stream(columns_str);
            std::string col;
            while (std::getline(columns_stream, col, ',')) {
                col.erase(col.begin(), std::find_if(col.begin(), col.end(),
                            [](unsigned char ch) { return !std::isspace(ch); }));
                col.erase(std::find_if(col.rbegin(), col.rend(),
                            [](unsigned char ch) { return !std::isspace(ch); }).base(), col.end());
                pq.select_columns.push_back(col);
            }

            if (matches[3].matched) {
                ParsedQuery::JoinInfo join_info;
                join_info.table_name = matches[3].str();
                std::string join_condition_str = matches[4].str();

                ExpressionParser expr_parser(join_condition_str);
                join_info.join_condition = expr_parser.parse_expression();

                pq.joins.push_back(std::move(join_info));
            }

            if (matches[5].matched) {
                std::string where_clause_str = matches[5].str();
                ExpressionParser expr_parser(where_clause_str);
                pq.where_clause = expr_parser.parse_expression();
            }
        } else {
            throw std::invalid_argument("Invalid SELECT syntax.");
        }
    }
    else if (command == "update") {
        pq.type = ParsedQuery::QueryType::Update;
        std::string table_name;
        iss >> table_name;
        pq.table_name = table_name;

        std::string set_word;
        iss >> set_word;
        std::transform(set_word.begin(), set_word.end(), set_word.begin(), ::tolower);
        if (set_word != "set") {
            throw std::invalid_argument("Expected 'set' after table name in UPDATE.");
        }

        // Parse assignments
        std::string assignments_str;
        std::getline(iss, assignments_str, 'w'); // 'w' as in 'where'
        // 'where'
        std::string where_part;
        std::getline(iss, where_part);
        size_t where_pos = assignments_str.find("where");
        if (where_pos != std::string::npos) {
            assignments_str = assignments_str.substr(0, where_pos);
        }

        std::stringstream assign_stream(assignments_str);
        std::string assign;
        while (std::getline(assign_stream, assign, ',')) {
            size_t eq_pos = assign.find('=');
            if (eq_pos == std::string::npos) {
                throw std::invalid_argument("Invalid assignment in UPDATE.");
            }
            std::string col_name = assign.substr(0, eq_pos);
            std::string expr_str = assign.substr(eq_pos + 1);

            size_t first = col_name.find_first_not_of(" \t\r\n");
            size_t last = col_name.find_last_not_of(" \t\r\n");
            if (first != std::string::npos && last != std::string::npos) {
                col_name = col_name.substr(first, last - first + 1);
            }

            first = expr_str.find_first_not_of(" \t\r\n");
            last = expr_str.find_last_not_of(" \t\r\n");
            if (first != std::string::npos && last != std::string::npos) {
                expr_str = expr_str.substr(first, last - first + 1);
            }

            ExpressionParser expr_parser(expr_str);
            pq.update_assignments[col_name] = expr_parser.parse_expression();
        }

        // Parse WHERE
        if (where_pos != std::string::npos) {
            std::string condition_str = assignments_str.substr(where_pos + 5); // 5 symbols 'where'
            size_t first = condition_str.find_first_not_of(" \t\r\n");
            size_t last = condition_str.find_last_not_of(" \t\r\n");
            if (first != std::string::npos && last != std::string::npos) {
                condition_str = condition_str.substr(first, last - first + 1);
            }
            ExpressionParser expr_parser(condition_str);
            pq.update_where_clause = expr_parser.parse_expression();
        }
    }
    else if (command == "delete") {
        pq.type = ParsedQuery::QueryType::Delete;
        std::string from_word;
        iss >> from_word;
        std::transform(from_word.begin(), from_word.end(), from_word.begin(), ::tolower);
        if (from_word != "from") {
            throw std::invalid_argument("Expected 'from' after 'delete'.");
        }

        std::string table_name;
        iss >> table_name;
        pq.table_name = table_name;

        // Parse WHERE
        std::string where_word;
        iss >> where_word;
        std::transform(where_word.begin(), where_word.end(), where_word.begin(), ::tolower);
        if (where_word != "where") {
            throw std::invalid_argument("Expected 'where' in DELETE.");
        }

        std::string condition_str;
        std::getline(iss, condition_str);
        size_t first = condition_str.find_first_not_of(" \t\r\n");
        size_t last = condition_str.find_last_not_of(" \t\r\n");
        if (first != std::string::npos && last != std::string::npos) {
            condition_str = condition_str.substr(first, last - first + 1);
        }
        ExpressionParser expr_parser(condition_str);
        pq.delete_where_clause = expr_parser.parse_expression();
    }
    else {
        throw std::invalid_argument("Unknown command: " + command);
    }

    std::cout << "Parsed Columns for Table " << pq.table_name << ":" << std::endl;
    for (const auto& column : pq.columns) {
        std::cout << "  " << column.to_string() << std::endl;
    }

    return pq;
}

Column QueryParser::parse_column_definition(const std::string& col_def) {
    std::vector<ColumnAttribute> attributes;
    std::optional<Value> default_value = std::nullopt;
    std::string col_name;
    DataType data_type(Type::Int64);

    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::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);
            }
        }
        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 name_part = col_def.substr(start, colon_pos - start);
    name_part.erase(name_part.begin(), std::find_if(name_part.begin(), name_part.end(),
                    [](unsigned char ch) { return !std::isspace(ch); }));
    name_part.erase(std::find_if(name_part.rbegin(), name_part.rend(),
                    [](unsigned char ch) { return !std::isspace(ch); }).base(), name_part.end());

    if (name_part.empty()) {
        throw std::invalid_argument("Column name is empty.");
    }
    col_name = name_part;

    size_t eq_pos = col_def.find('=', colon_pos + 1);
    std::string type_str;
    if (eq_pos != std::string::npos) {
        type_str = col_def.substr(colon_pos + 1, eq_pos - colon_pos - 1);
        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(eq_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(colon_pos + 1);
        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("int64") != std::string::npos) {
        type = Type::Int64;
    }
    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 if (type_str.find("bool") != std::string::npos) {
        type = Type::Bool;
    }
    else {
        throw std::invalid_argument("Unknown column type: " + type_str);
    }

    if (type == Type::String || type == Type::Bytes) {
        data_type = DataType(type, size);
    }
    else {
        data_type = DataType(type);
    }

    for (const auto& attr : attributes) {
        switch (attr) {
            case ColumnAttribute::Unique:
                std::cout << "unique ";
                break;
            case ColumnAttribute::AutoIncrement:
                std::cout << "autoincrement ";
                break;
            case ColumnAttribute::Key:
                std::cout << "key ";
                break;
            default:
                std::cout << "unknown ";
        }
    }
    if (default_value) {
        std::cout << ", Default: " << default_value->to_string();
    }
    std::cout << std::endl;

    return Column(col_name, data_type, attributes, default_value);
}

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 {
        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);
        }
    }
}

std::vector<std::string> QueryParser::split_columns(const std::string& columns_def) {
    std::vector<std::string> columns;
    int brace_level = 0;
    std::string current;
    
    for (char ch : columns_def) {
        if (ch == '{') {
            brace_level++;
            current += ch;
        }
        else if (ch == '}') {
            brace_level--;
            current += ch;
        }
        else if (ch == ',' && brace_level == 0) {
            size_t first = current.find_first_not_of(" \t\r\n");
            size_t last = current.find_last_not_of(" \t\r\n");
            if (first != std::string::npos && last != std::string::npos) {
                columns.emplace_back(current.substr(first, last - first + 1));
            }
            current.clear();
        }
        else {
            current += ch;
        }
    }
    
    size_t first = current.find_first_not_of(" \t\r\n");
    size_t last = current.find_last_not_of(" \t\r\n");
    if (first != std::string::npos && last != std::string::npos) {
        columns.emplace_back(current.substr(first, last - first + 1));
    }
    
    return columns;
}

std::string QueryParser::trim(const std::string& s) {
    auto start = std::find_if_not(s.begin(), s.end(), [](unsigned char ch) {
        return std::isspace(ch);
    });
    auto end = std::find_if_not(s.rbegin(), s.rend(), [](unsigned char ch) {
        return std::isspace(ch);
    }).base();
    
    if (start >= end) {
        return "";
    }
    return std::string(start, end);
}

} // namespace core
} // namespace memdb
```
QueryResult.cpp
```
#include "memdb/core/QueryResult.h"
```
Row.cpp
```
#include "memdb/core/Row.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"

#include "memdb/core/exceptions/DatabaseException.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_int64()) {
            row.get_values().emplace_back(int64_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);

    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;
}

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

    std::vector<std::optional<Value>> complete_values = values;

    for (size_t i = 0; i < columns_.size(); ++i) {
        const auto& column = columns_[i];

        if (column.has_attribute(ColumnAttribute::AutoIncrement) && !complete_values[i].has_value()) {
            complete_values[i] = static_cast<int64_t>(next_row_id_);
        }
        else if (!complete_values[i].has_value() && column.get_default_value()) {
            complete_values[i] = *(column.get_default_value());
        }
    }

    RowID new_id;
    if (id != 0) {
        new_id = id;
        if (new_id >= next_row_id_) {
            next_row_id_ = new_id + 1;
        }
    }
    else {
        new_id = next_row_id_++;
    }

    for (size_t i = 0; i < columns_.size(); ++i) {
        const auto& column = columns_[i];
        if (column.has_attribute(ColumnAttribute::Unique) || column.has_attribute(ColumnAttribute::Key)) {
            if (complete_values[i].has_value()) {
                const Value& new_val = *(complete_values[i]);

                for (const auto& [existing_id, existing_row] : rows_) {
                    if (existing_id == new_id) {
                        continue;
                    }

                    const auto& existing_val_opt = existing_row.get_value(i);
                    if (existing_val_opt.has_value()) {
                        const Value& existing_val = *(existing_val_opt);

                        if (new_val.get_type() != existing_val.get_type()) {
                            continue;
                        }

                        bool equal = false;
                        switch (new_val.get_type()) {
                            case Type::Int32:
                                equal = (new_val.get_int() == existing_val.get_int());
                                break;
                            case Type::Int64:
                                equal = (new_val.get_int64() == existing_val.get_int64());
                                break;
                            case Type::Bool:
                                equal = (new_val.get_bool() == existing_val.get_bool());
                                break;
                            case Type::String:
                                equal = (new_val.get_string() == existing_val.get_string());
                                break;
                            case Type::Bytes:
                                equal = (new_val.get_bytes() == existing_val.get_bytes());
                                break;
                            default:
                                break;
                        }

                        if (equal) {
                            throw std::invalid_argument("Duplicate value for unique/key column '" + column.get_name() + "'.");
                        }
                    }
                }
            }
        }
    }

    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));
}

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

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


void Table::add_index(const std::string& index_type_str, const std::vector<std::string>& columns) {
    IndexType index_type;
    if (index_type_str == "ordered") {
        index_type = IndexType::Ordered;
    }
    else if (index_type_str == "unordered") {
        index_type = IndexType::Unordered;
    }
    else {
        throw std::invalid_argument("Unknown index type: " + index_type_str);
    }

    std::unique_ptr<Index> index = std::make_unique<Index>(index_type, columns);

    for (const auto& [row_id, row] : rows_) {
        std::unordered_map<std::string, Value> row_map;
        for (const auto& col_name : columns) {
            size_t col_index = get_column_index(col_name);
            if (row.get_values()[col_index].has_value()) {
                row_map[col_name] = *(row.get_values()[col_index]);
            }
            else {
                throw std::invalid_argument("Cannot index NULL value in column '" + col_name + "'.");
            }
        }
        index->add_row(row_id, row_map);
    }

    indexes_.emplace_back(std::move(index));
}

std::vector<RowID> Table::find_rows(const std::unique_ptr<Expression>& condition) const {
    std::vector<RowID> matching_rows;
    for (const auto& [row_id, row] : rows_) {
        std::unordered_map<std::string, Value> row_map;
        for (size_t i = 0; i < columns_.size(); ++i) {
            if (row.get_values()[i].has_value()) {
                row_map[columns_[i].get_name()] = *(row.get_values()[i]);
            }
        }

        bool match = true;
        if (condition) {
            Value cond = condition->evaluate(row_map);
            if (cond.get_type() != Type::Bool) {
                throw std::invalid_argument("WHERE clause does not evaluate to a boolean.");
            }
            match = cond.get_bool();
        }

        if (match) {
            matching_rows.push_back(row_id);
        }
    }

    return matching_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;
        }

        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());
        }

        // for String and 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.");
            }
        }
    }

    for (size_t i = 0; i < values.size(); ++i) {
        if (!values[i].has_value()) {
            continue;
        }

        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::Int64:
                                equal = (new_value.get_int64() == existing_value.get_int64());
                                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()) {
                switch (value->get_type()) {
                    case Type::Int32:
                        row_json["values"].push_back(value->get_int());
                        break;
                    case Type::Int64:
                        row_json["values"].push_back(value->get_int64());
                        break;
                    case Type::Bool:
                        row_json["values"].push_back(value->get_bool());
                        break;
                    case Type::String:
                        row_json["values"].push_back(value->get_string());
                        break;
                    case Type::Bytes:
                        {
                            const auto& bytes = value->get_bytes();
                            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];
                            }
                            row_json["values"].push_back(hex_str);
                        }
                        break;
                    default:
                        row_json["values"].push_back(nullptr);
                        break;
                }
            }
            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 type;
        size_t size = 0;
        if (col_type_str.find("int32") != std::string::npos) {
            type = Type::Int32;
        }
        else if (col_type_str.find("int64") != std::string::npos) {
            type = Type::Int64;
        }
        else if (col_type_str.find("bool") != std::string::npos) {
            type = Type::Bool;
        }
        else if (col_type_str.find("string") != std::string::npos) {
            type = Type::String;
            size = std::stoul(col_type_str.substr(col_type_str.find('[') + 1,
                        col_type_str.find_last_of(']') - col_type_str.find('[') - 1));
        }
        else if (col_type_str.find("bytes") != std::string::npos) {
            type = Type::Bytes;
            size = std::stoul(col_type_str.substr(col_type_str.find('[') + 1,
                        col_type_str.find_last_of(']') - col_type_str.find('[') - 1));
        }
        else {
            throw exceptions::SerializationException("Unknown column type: " + col_type_str);
        }

        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 {
                // TODO: unknown attributes
            }
        }

        std::optional<Value> default_value = std::nullopt;
        if (col_json.contains("default")) {
            std::string default_val_str = col_json.at("default").get<std::string>();
            switch (type) {
                case Type::Int32:
                    default_value = Value(static_cast<int32_t>(std::stoi(default_val_str)));
                    break;
                case Type::Int64:
                    default_value = Value(static_cast<int64_t>(std::stoll(default_val_str)));
                    break;
                case Type::Bool:
                    default_value = Value((default_val_str == "true"));
                    break;
                case Type::String:
                    if (default_val_str.front() == '"' && default_val_str.back() == '"') {
                        default_val_str = default_val_str.substr(1, default_val_str.size() - 2);
                    }
                    default_value = Value(default_val_str);
                    break;
                case Type::Bytes:
                {
                    std::string hex_str = default_val_str;
                    if (hex_str.find("0x") != 0 && hex_str.find("0X") != 0) {
                        throw exceptions::SerializationException("Invalid bytes format.");
                    }
                    hex_str = hex_str.substr(2);
                    if (hex_str.size() % 2 != 0) {
                        throw exceptions::SerializationException("Invalid hex length for bytes.");
                    }
                    std::vector<uint8_t> bytes;
                    bytes.reserve(hex_str.size() / 2);
                    for (size_t i = 0; i < hex_str.size(); i += 2) {
                        uint8_t byte = static_cast<uint8_t>(std::stoi(hex_str.substr(i, 2), nullptr, 16));
                        bytes.push_back(byte);
                    }
                    default_value = Value(bytes);
                }
                break;
                default:
                    throw exceptions::SerializationException("Unhandled column type: " + col_type_str);
            }
        }

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

        columns.emplace_back(col_name, data_type, 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;
        const auto& cols = table.get_columns();
        const auto& val_array = row_json.at("values");

        for (size_t i = 0; i < cols.size(); ++i) {
            const auto& col = cols[i];
            const auto& val_json = val_array[i];

            if (val_json.is_null()) {
                values.emplace_back(std::nullopt);
            }
            else {
                switch (col.get_type().get_type()) {
                    case Type::Int32:
                        values.emplace_back(Value(static_cast<int32_t>(val_json.get<int32_t>())));
                        break;
                    case Type::Int64:
                        values.emplace_back(Value(static_cast<int64_t>(val_json.get<int64_t>())));
                        break;
                    case Type::Bool:
                        values.emplace_back(Value(val_json.get<bool>()));
                        break;
                    case Type::String:
                        values.emplace_back(Value(val_json.get<std::string>()));
                        break;
                    case Type::Bytes:
                        {
                            std::string hex_str = val_json.get<std::string>();
                            if (hex_str.find("0x") != 0 && hex_str.find("0X") != 0) {
                                throw exceptions::SerializationException("Invalid bytes format.");
                            }
                            hex_str = hex_str.substr(2);
                            if (hex_str.size() % 2 != 0) {
                                throw exceptions::SerializationException("Invalid hex length for bytes.");
                            }
                            std::vector<uint8_t> bytes;
                            bytes.reserve(hex_str.size() / 2);
                            for (size_t j = 0; j < hex_str.size(); j += 2) {
                                uint8_t byte = static_cast<uint8_t>(std::stoi(hex_str.substr(j, 2), nullptr, 16));
                                bytes.push_back(byte);
                            }
                            values.emplace_back(Value(bytes));
                        }
                        break;
                    default:
                        throw exceptions::SerializationException("Unknown column type: " + col.get_type().to_string());
                }
            }
        }

        table.insert_row(values, row_id);
    }

    return table;
}

std::string Table::to_string() const {
    std::ostringstream oss;
    oss << "Table: " << name_ << "\nColumns:\n";
    for (const auto& column : columns_) {
        oss << "  " << column.to_string() << "\n";
    }
    oss << "Rows:\n";
    for (const auto& [id, row] : rows_) {
        oss << "  RowID " << id << ": ";
        for (size_t i = 0; i < row.get_values().size(); ++i) {
            if (i > 0) oss << ", ";
            if (row.get_values()[i].has_value()) {
                oss << row.get_values()[i]->to_string();
            } else {
                oss << "NULL";
            }
        }
        oss << "\n";
    }
    return oss.str();
}

} // 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());
}

bool Value::has_value() const {
    return data_.has_value();
}

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_);
}

const std::variant<int32_t, int64_t, bool, std::string, std::vector<uint8_t>>& Value::get_variant() const {
    if (!data_) {
        throw exceptions::TypeMismatchException("Attempted to get variant of NULL Value.");
    }
    return *data_;
}

std::variant<int32_t, int64_t, bool, std::string, std::vector<uint8_t>>& Value::get_variant() {
    if (!data_) {
        throw exceptions::TypeMismatchException("Attempted to get variant of NULL Value.");
    }
    return *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 <iostream>

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

    try {
        // Создание таблицы users
        std::string create_users_query = R"(
            create table users (
                {autoincrement, key} id : int64,
                {unique} login : string[32],
                password_hash : bytes[8],
                is_admin : bool = false
            )
        )";

        memdb::core::QueryResult result = db.execute(create_users_query);
        if (!result.is_ok()) {
            std::cerr << "Error creating table 'users': " << result.get_error() << std::endl;
            return 1;
        }
        std::cout << "Table 'users' created successfully." << std::endl;

        // Создание таблицы posts
        std::string create_posts_query = R"(
            create table posts (
                {autoincrement, key} id : int64,
                user_id : int64,
                content : string[255]
            )
        )";

        result = db.execute(create_posts_query);
        if (!result.is_ok()) {
            std::cerr << "Error creating table 'posts': " << result.get_error() << std::endl;
            return 1;
        }
        std::cout << "Table 'posts' created successfully." << std::endl;

        // Вставка данных в таблицу users
        std::string insert_users_query = R"(
            insert (, "john_doe", 0xdeadbeefdeadbeef, false) to users
        )";
        result = db.execute(insert_users_query);
        if (!result.is_ok()) {
            std::cerr << "Error inserting into 'users': " << result.get_error() << std::endl;
            return 1;
        }
        std::cout << "Inserted 'john_doe' into 'users'." << std::endl;

        // Вставляем несколько пользователей
        db.execute(R"(insert (, "alice", 0xabcdefabcdefabcd, false) to users)");
        db.execute(R"(insert (, "bob", 0x1234567812345678, true) to users)");
        std::cout << "Inserted 'alice' and 'bob' into 'users'." << std::endl;

        // Вставка данных в таблицу posts
        db.execute(R"(insert (, 1, "Hello, world!") to posts)");
        db.execute(R"(insert (, 2, "This is a post from Alice.") to posts)");
        db.execute(R"(insert (, 1, "Another post from John.") to posts)");
        std::cout << "Inserted posts into 'posts'." << std::endl;

        // Создание индекса
        std::string create_index_query = R"(
            create ordered index on users by login
        )";
        result = db.execute(create_index_query);
        if (!result.is_ok()) {
            std::cerr << "Error creating index on 'users': " << result.get_error() << std::endl;
            return 1;
        }
        std::cout << "Index on 'users' created successfully." << std::endl;

        // Выполнение SELECT запроса
        std::string select_query = R"(
            select id, login, is_admin from users where is_admin = false
        )";
        result = db.execute(select_query);
        if (!result.is_ok()) {
            std::cerr << "Error selecting from 'users': " << result.get_error() << std::endl;
            return 1;
        }
        std::cout << "Selected users where is_admin = false:" << std::endl;
        const auto& data = result.get_data();
        for (const auto& row : data) {
            int64_t id = row[0]->get_int64();
            std::string login = row[1]->get_string();
            bool is_admin = row[2]->get_bool();
            std::cout << "ID: " << id << ", Login: " << login << ", Is Admin: " << (is_admin ? "true" : "false") << std::endl;
        }

        // Выполнение UPDATE запроса
        std::string update_query = R"(
            update users set is_admin = true where login = "john_doe"
        )";
        result = db.execute(update_query);
        if (!result.is_ok()) {
            std::cerr << "Error updating 'users': " << result.get_error() << std::endl;
            return 1;
        }
        std::cout << "Updated 'john_doe' to be an admin." << std::endl;

        // Выполнение DELETE запроса
        std::string delete_query = R"(
            delete from users where login = "alice"
        )";
        result = db.execute(delete_query);
        if (!result.is_ok()) {
            std::cerr << "Error deleting from 'users': " << result.get_error() << std::endl;
            return 1;
        }
        std::cout << "Deleted 'alice' from 'users'." << std::endl;

        // Выполнение JOIN запроса
        std::string join_query = R"(
            select posts.id, users.login, posts.content from users join posts on users.id = posts.user_id where true
        )";
        result = db.execute(join_query);
        if (!result.is_ok()) {
            std::cerr << "Error executing join query: " << result.get_error() << std::endl;
            return 1;
        }
        std::cout << "Joined 'users' and 'posts':" << std::endl;
        const auto& join_data = result.get_data();
        for (const auto& row : join_data) {
            int64_t post_id = row[0]->get_int64();
            std::string user_login = row[1]->get_string();
            std::string content = row[2]->get_string();
            std::cout << "Post ID: " << post_id << ", User: " << user_login << ", Content: " << content << 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;
}
```
я получаю такую ошибку
```
kirill@kirill-BRN-FXX:~/Desktop/memdb_project/build$ ./example_basic_usage 
Parsing Column Definition: {autoincrement, key} id : int64
autoincrement key 
Parsing Column Definition: {unique} login : string[32]
unique 
Parsing Column Definition: password_hash : bytes[8]

Parsing Column Definition: is_admin : bool = false
, Default: false
Parsed Columns for Table users:
  {autoincrement, key} id : int64
  {unique} login : string[32]
  password_hash : bytes[8]
  is_admin : bool = false
Table 'users' created successfully.
Parsing Column Definition: {autoincrement, key} id : int64
autoincrement key 
Parsing Column Definition: user_id : int64

Parsing Column Definition: content : string[255]

Parsed Columns for Table posts:
  {autoincrement, key} id : int64
  user_id : int64
  content : string[255]
Table 'posts' created successfully.
Parsed Columns for Table users:
Inserted 'john_doe' into 'users'.
Parsed Columns for Table users:
Parsed Columns for Table users:
Inserted 'alice' and 'bob' into 'users'.
Parsed Columns for Table posts:
Parsed Columns for Table posts:
Inserted posts into 'posts'.
Parsed Columns for Table users:
Index on 'users' created successfully.
Parsed Columns for Table users:
Selected users where is_admin = false:
ID: 2, Login: alice, Is Admin: false
ID: 1, Login: john_doe, Is Admin: false
Parsed Columns for Table users:
Updated 'john_doe' to be an admin.
Parsed Columns for Table users:
Deleted 'alice' from 'users'.
Error executing join query: Unknown error during query execution.
```
что не так с join-ом? нормально, что я получаю сначала ID: 2, а потом - ID: 1?