я отправляю тебе задание, скажи, какую последнюю строчку ты получишь.
Общее описание задачи на 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
│       │   ├── QueryResultIterator.h
│       │   ├── ResultRow.h
│       │   ├── Row.h
│       │   ├── Table.h
│       │   ├── Value.h
│       │   ├── exceptions/
│       │   │	├── DatabaseException.h
│       │   │	└── TypeMismatchException.h
│       │   ├── structs/
│       │   │	├── Token.h
│       │   │	├── ColumnInfo.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
│       ├── QueryResultIterator.cpp
│       ├── ResultRow.cpp
│       ├── Row.cpp
│       ├── Table.cpp
│       └── Value.cpp
├── tests/
│   └── test_core/
│       ├── TestCreateTable.cpp
│       └── TestInsert.cpp
├── 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_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 "memdb/core/DataType.h"

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

namespace memdb {
namespace core {

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

class LiteralExpression : public Expression {
public:
    LiteralExpression(const Value& value);
    DataType get_type() const override;
    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);
    DataType get_type() const override;
    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);
    DataType get_type() const override;
    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);
    DataType get_type() const override;
    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_logical_xor();
    std::unique_ptr<Expression> parse_equality();
    std::unique_ptr<Expression> parse_comparison();
    std::unique_ptr<Expression> parse_addition();
    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();
    
    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_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 Database;

class QueryParser {
public:
    ParsedQuery parse(const std::string& query);
    bool validate_query(const std::string& query);

    void set_database(Database* db);

private:
    Database* db_ = nullptr;
    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 "memdb/core/QueryResultIterator.h"

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

#include <vector>
#include <string>
#include <optional>
#include <iterator>

namespace memdb {
namespace core {

class QueryResult {
public:
    QueryResult() : success_(true) {}
    QueryResult(const std::vector<std::vector<std::optional<Value>>>& data) 
                                : success_(true), data_(data), columns_() {}
    QueryResult(const std::vector<std::vector<std::optional<Value>>>& data, 
                const std::vector<ColumnInfo>& columns) : success_(true), data_(data), columns_(columns) {}
    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_; }

    QueryResultIterator begin() const;
    QueryResultIterator end() const;

    const std::vector<std::vector<std::optional<Value>>>& get_data() const { return data_; }
    const std::vector<ColumnInfo>& get_columns() const { return columns_; }

    std::string to_string() const;
    void print(std::ostream& os = std::cout) const;

    friend std::ostream& operator<<(std::ostream& os, const QueryResult& result);

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

    void print_separator(std::ostringstream& ss, const std::vector<size_t>& widths) const;
    size_t get_max_value_width(size_t col_idx) const;
    static std::string type_to_string(Type type);
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_QUERYRESULT_H
```
QueryResultIterator.h
```
#ifndef MEMDB_CORE_QUERYRESULTITERATOR_H
#define MEMDB_CORE_QUERYRESULTITERATOR_H

#include "memdb/core/ResultRow.h"
#include "memdb/core/structs/ColumnInfo.h"
#include <vector>

namespace memdb {
namespace core {

class QueryResultIterator {
public:
    using DataIterator = std::vector<std::vector<std::optional<Value>>>::const_iterator;

    QueryResultIterator(DataIterator data_iter, const std::vector<ColumnInfo>& columns)
                        : data_iter_(data_iter), columns_(&columns) {}

    ResultRow operator*() const;
    QueryResultIterator& operator++();
    bool operator!=(const QueryResultIterator& other) const;

private:
    DataIterator data_iter_;
    const std::vector<ColumnInfo>* columns_;
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_QUERYRESULTITERATOR_H
```
ResultRow.h
```
#ifndef MEMDB_CORE_RESULTROW_H
#define MEMDB_CORE_RESULTROW_H

#include "memdb/core/Value.h"
#include "memdb/core/structs/ColumnInfo.h"

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

namespace memdb {
namespace core {

class ResultRow {
public:
    ResultRow(const std::vector<std::optional<Value>>& values, const std::vector<ColumnInfo>& columns);

    const std::optional<Value>& operator[](const std::string& column_name) const;

private:
    std::unordered_map<std::string, std::optional<Value>> row_map_;
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_RESULTROW_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 = uint32_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(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;
    bool get_bool() const;
    const std::string& get_string() const;
    const std::vector<uint8_t>& get_bytes() const;

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

    void set_int(int32_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, 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,
    IntLiteral,
    BoolLiteral,
    StringLiteral,
    BytesLiteral,
    Operator,
    Length,
    Comma,
    LeftParen,
    RightParen,
    EndOfInput
};

} // namespace core
} // namespace memdb

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

namespace memdb {
namespace core {

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

} // 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/ColumnInfo.h
```
#ifndef MEMDB_CORE_STRUCTS_COLUMNINFO_H
#define MEMDB_CORE_STRUCTS_COLUMNINFO_H

#include "memdb/core/enums/Type.h"
#include "memdb/core/DataType.h"

#include <string>

namespace memdb {
namespace core {

struct ColumnInfo {
    std::string name;
    DataType type;

    ColumnInfo(const std::string& name_, Type type_) : name(name_), type(type_) {}
    ColumnInfo(const std::string& name_, const DataType& data_type) : name(name_), type(data_type) {} 

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

    Type get_type() const { return type.get_type(); }
};

} // namespace core
} // namespace memdb

#endif // MEMDB_CORE_STRUCTS_COLUMNINFO_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;

    struct SelectItem {
        std::unique_ptr<Expression> expression;
        std::string alias; // Optional alias; can be empty if no alias is specified
    };
    std::vector<SelectItem> select_items;
    
    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 if needed
};

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

RowID Database::insert_row(const std::string& table_name, const std::vector<std::optional<Value>>& values) {
    auto table = get_table(table_name);
    if (!table) {
        throw std::invalid_argument("Table does not exist: " + table_name);
    }

    const auto& columns = table->get_columns();
    std::vector<std::optional<Value>> final_values;
    final_values.reserve(columns.size());

    // Validate types and sizes
    for (size_t i = 0; i < columns.size(); i++) {
        const auto& column = columns[i];
        
        if (i >= values.size() || !values[i].has_value()) {
            if (column.has_attribute(ColumnAttribute::AutoIncrement)) {
                final_values.emplace_back(); // Leave empty for auto-increment
                continue;
            }
            if (column.get_default_value().has_value()) {
                final_values.emplace_back(column.get_default_value());
                continue;
            }
            throw std::invalid_argument("Missing value for column: " + column.get_name());

        }

        const auto& value = values[i].value();
        
        if (value.get_type() != column.get_type().get_type()) {
            throw std::invalid_argument("Invalid type for column '" + column.get_name() + "'");
        }

        if (column.get_type().get_type() == Type::String || 
            column.get_type().get_type() == Type::Bytes) {
            size_t value_size = (column.get_type().get_type() == Type::String) ? 
                                value.get_string().size() : 
                                value.get_bytes().size();
            if (value_size > column.get_type().get_size()) {
                std::string type_of_str = (column.get_type().get_type() == Type::String ? "String" : "Bytes");
                throw std::invalid_argument(
                    type_of_str +
                    " value exceeds defined size of " + std::to_string(column.get_type().get_size()) +
                    " for column \"" + column.get_name() + "\"");
            }
        }
        
        final_values.push_back(values[i]);
    }

    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 (const std::exception& e) {
        return QueryResult(std::string("Standard exception: ") + 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_bool() const {
    return type_ == Type::Bool;
}

std::string DataType::to_string() const {
    switch (type_) {
        case Type::Int32:
            return "int32";
        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::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_;
}

DataType LiteralExpression::get_type() const {
    return value_.get_type();
}

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

DataType VariableExpression::get_type() const {
    return Type::Unknown;
}

// ======= 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 '|var|' requires String or Bytes type.");
            }
        default:
            throw std::runtime_error("Unknown unary operator.");
    }
}

DataType UnaryExpression::get_type() const {
    switch (op_) {
        case Operator::Not:
            return Type::Bool;
        case Operator::Length:
            return Type::Int32;
        default:
            return Type::Unknown;
    }
}

// ======= 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::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());
            }
            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());
            }
            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());
            }
            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());
            }
            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(static_cast<bool>(left_val.get_bool() ^ right_val.get_bool()));

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

DataType BinaryExpression::get_type() const {
    switch (op_) {
        case Operator::Add:
        case Operator::Subtract:
        case Operator::Multiply:
        case Operator::Divide:
        case Operator::Modulo:
            return Type::Int32;
        case Operator::Less:
        case Operator::LessEqual:
        case Operator::Greater:
        case Operator::GreaterEqual:
        case Operator::Equal:
        case Operator::NotEqual:
        case Operator::And:
        case Operator::Or:
        case Operator::Xor:
            return Type::Bool;
        default:
            return Type::Unknown;
    }
}

} // 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_xor();
    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_xor() {
    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::Xor, 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));
    }

    return parse_primary();
}

std::unique_ptr<Expression> ExpressionParser::parse_primary() {
    if (match(TokenType::Length)) {
        std::string var_name = current_token_.value;
        advance();

        auto var_expr = std::make_unique<VariableExpression>(var_name);

        return std::make_unique<UnaryExpression>(UnaryExpression::Operator::Length, std::move(var_expr));
    }

    if (match(TokenType::LeftParen)) {
        advance();
        auto expr = parse_expression();
        consume(TokenType::RightParen, "Expected ')' after expression.");
        return expr;
    }

    if (match(TokenType::IntLiteral)) {
        int32_t value = std::stoll(current_token_.value);
        advance();
        return std::make_unique<LiteralExpression>(Value(static_cast<int32_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>
#include <iostream>

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 (current == '0' && (input_.size() > pos_ + 1) && (input_[pos_ + 1] == 'x' || input_[pos_ + 1] == 'X')) {
        return bytes_literal();
    }

    if (std::isdigit(current) || 
        ((current == '-' || current == '+') && 
         (pos_ + 1 < input_.size()) && 
         std::isdigit(input_[pos_ + 1]))) {
        Token tok = number();
        return tok;
    }

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

    std::string var_name;  // for Length operator

    switch (current) {
        case ',': get_char(); return Token{TokenType::Comma, ","};
        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, "||"};
            }

            while (std::isalnum(peek()) || peek() == '_') {
                var_name += get_char();
            }

            if (peek() == '|') {
                get_char();
                return Token{TokenType::Length, var_name};
            }
            else {
                throw exceptions::TypeMismatchException("Invalid operator after '|': expected '|'");
            }
            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() == '_' || peek() == '.') { // Include dot
        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();
    }

    return Token{TokenType::IntLiteral, 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};
}

} // 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 "memdb/core/structs/ColumnInfo.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::TypeMismatchException& e) {
                return QueryResult(e.what());
            }
            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<int32_t>(new_row_id)) } };
                return QueryResult(data);
            }
            catch (const exceptions::DatabaseException& e) {
                return QueryResult(e.what());
            }
            break;
        
        case ParsedQuery::QueryType::Select:
            return execute_select(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::vector<std::vector<std::optional<Value>>> results;
        std::vector<ColumnInfo> result_columns;

        if (!pq.joins.empty()) {
            auto table1 = db.get_table(pq.table_name);
            auto table2 = db.get_table(pq.joins[0].table_name);

            const auto& columns1 = table1->get_columns();
            const auto& columns2 = table2->get_columns();

            std::vector<ColumnInfo> aliased_columns1;
            for (const auto& col : columns1) {
                std::string aliased_name = pq.table_name + "." + col.get_name();
                aliased_columns1.emplace_back(ColumnInfo(aliased_name, col.get_type()));
                result_columns.emplace_back(ColumnInfo(aliased_name, col.get_type()));
            }

            std::vector<ColumnInfo> aliased_columns2;
            for (const auto& col : columns2) {
                std::string aliased_name = pq.joins[0].table_name + "." + col.get_name();
                aliased_columns2.emplace_back(ColumnInfo(aliased_name, col.get_type()));
                result_columns.emplace_back(ColumnInfo(aliased_name, col.get_type()));
            }

            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()) {
                        row_map[aliased_columns1[i].get_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()) {
                            combined_row_map[aliased_columns2[i].get_name()] = *(row2.get_values()[i]);
                        }
                    }

                    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()) {
                        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) {
                            std::vector<std::optional<Value>> selected_values;
                            for (const auto& select_item : pq.select_items) {
                                try {
                                    Value val = select_item.expression->evaluate(combined_row_map);
                                    selected_values.push_back(val);
                                } catch (const std::exception& e) {
                                    return QueryResult("Error evaluating expression in SELECT clause: " + std::string(e.what()));
                                }
                            }
                            results.push_back(selected_values);
                        }
                    }
                }
            }

            return QueryResult(results, result_columns);
        } else {
            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& select_item : pq.select_items) {

                DataType expr_type = select_item.expression->get_type(); 

                result_columns.emplace_back(ColumnInfo(select_item.alias, expr_type));
            }

            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& select_item : pq.select_items) {
                        try {
                            Value val = select_item.expression->evaluate(row_map);
                            selected_values.push_back(val);
                        } catch (const std::exception& e) {
                            return QueryResult("Error evaluating expression in SELECT clause: " + std::string(e.what()));
                        }
                    }
                    results.push_back(selected_values);
                }
            }

            return QueryResult(results, result_columns);
        }
    } 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<int32_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<int32_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/Table.h"
#include "memdb/core/Database.h"
#include "memdb/core/ExpressionParser.h"

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

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

namespace memdb {
namespace core {

bool QueryParser::validate_query(const std::string& query) {
    std::stack<char> s;
    for (char ch : query) {
        if (ch == '(' || ch == '{') {
            s.push(ch);
        } else if (ch == ')' || ch == '}') {
            if (s.empty()) {
                return false;
            }
            char open = s.top();
            s.pop();
            if ((ch == ')' && open != '(') || (ch == '}' && open != '{')) {
                return false;
            }
        }
    }
    return s.empty();
}

const std::unordered_set<std::string> reservedKeywords = {
    "create", "table", "insert", "update", "delete", "join", "where", "int32", "string", "bytes", "bool", "key", "unique", "autoincrement",  "index", "unordered", "ordered", "on", "select", "from", "values", "as"
};

bool isValidIdentifier(const std::string& identifier) {
    std::regex valid_identifier_regex("^[a-zA-Z][a-zA-Z0-9_]*$");
    bool regex_match = std::regex_match(identifier, valid_identifier_regex);
    if (!regex_match) {
        return false;
    }
    
    std::string lower_identifier = identifier;
    std::transform(lower_identifier.begin(), lower_identifier.end(), lower_identifier.begin(), ::tolower);
    return reservedKeywords.find(lower_identifier) == reservedKeywords.end();
}

void QueryParser::set_database(Database* db) {
    db_ = db;
}

Value parse_value(TokenType token_type, const std::string& value_str, size_t size = 0) {
    switch (token_type) {
        case TokenType::StringLiteral:
            if (size > 0 && value_str.size() > size) {
                throw std::invalid_argument("String value exceeds defined size of " + 
                    std::to_string(size));
            }
            
            return Value(value_str);

        case TokenType::BytesLiteral: {
            std::string hex_str = value_str.substr(2);
            if (hex_str.size() % 2 != 0) {
                throw std::invalid_argument("Invalid hex string for bytes.");
            }
            size_t bytes_size = hex_str.size() / 2;
            if (bytes_size > size) {
                throw std::invalid_argument("Bytes value exceeds defined size of " + 
                    std::to_string(size));
            }
            std::vector<uint8_t> bytes;
            bytes.reserve(bytes_size);
            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);
        }

        case TokenType::BoolLiteral:
            return Value(value_str == "true");

        case TokenType::IntLiteral:
            try {
                int32_t int_val = std::stoi(value_str);
                return Value(int_val);
            } catch (const std::exception&) {
                throw std::invalid_argument("Invalid integer value: " + value_str);
            }

        default:
            throw std::invalid_argument("Invalid value: " + value_str);
    }
}

std::vector<std::optional<Value>> parse_values(const std::string& values_str, const std::vector<Column>& columns) {
    std::vector<std::optional<Value>> values(columns.size(), std::nullopt);
    Lexer lexer(values_str);
    Token token = lexer.get_next_token();

    bool named_format = false;
    size_t peek_pos = values_str.find('=');
    if (peek_pos != std::string::npos) {
        named_format = true;
    }

    if (named_format) {
        std::unordered_set<std::string> processed_columns;
        while (token.type != TokenType::EndOfInput) {
            std::string column_name;
            while (token.type != TokenType::EndOfInput && token.type != TokenType::Operator && token.value != "=") {
                if (token.type == TokenType::Identifier) {
                    column_name = token.value;
                }
                token = lexer.get_next_token();
            }
            
            if (token.type != TokenType::Operator || token.value != "=") {
                throw std::invalid_argument("Expected '=' after column name");
            }
            token = lexer.get_next_token();

            if (processed_columns.find(column_name) != processed_columns.end()) {
                throw std::invalid_argument("Duplicate column name: " + column_name);
            }
            processed_columns.insert(column_name);
            
            size_t col_idx = 0;
            bool found = false;
            for (size_t i = 0; i < columns.size(); i++) {
                if (columns[i].get_name() == column_name) {
                    col_idx = i;
                    found = true;
                    break;
                }
            }
            if (!found) {
                throw std::invalid_argument("Column not found: " + column_name);
            }
            
            if (token.type == TokenType::StringLiteral || token.type == TokenType::BytesLiteral ||
                token.type == TokenType::IntLiteral || token.type == TokenType::BoolLiteral) {
                const Column& col = columns[col_idx];
                size_t size = (col.get_type().get_type() == Type::String || 
                             col.get_type().get_type() == Type::Bytes) ? col.get_type().get_size() : 0;
                values[col_idx] = parse_value(token.type, token.value, size);
            }
            
            token = lexer.get_next_token();
            if (token.type == TokenType::Comma) {
                token = lexer.get_next_token();
            }
        }

        for (size_t i = 0; i < columns.size(); ++i) {
            if (!values[i].has_value()) {
                if (columns[i].has_attribute(ColumnAttribute::AutoIncrement)) {
                    continue;
                }
                if (columns[i].get_default_value().has_value()) {
                    values[i] = columns[i].get_default_value();
                } else {
                    throw std::invalid_argument("Missing value for column: " + columns[i].get_name());
                }
            }
        }
    } else {
        size_t column_index = 0;
        while (token.type != TokenType::EndOfInput) {
            if (token.type == TokenType::Comma) {
                values[column_index] = std::nullopt;
                token = lexer.get_next_token();
                column_index++;
                continue;
            }

            if (column_index >= columns.size()) {
                throw std::invalid_argument("Too many values for table columns");
            }

            const Column& current_column = columns[column_index];
            const DataType& dtype = current_column.get_type();
            size_t size = 0;

            if (dtype.get_type() == Type::String || dtype.get_type() == Type::Bytes) {
                size = dtype.get_size();
            }

            if (token.type == TokenType::StringLiteral || token.type == TokenType::BytesLiteral ||
                token.type == TokenType::IntLiteral || token.type == TokenType::BoolLiteral) {
                if (!token.value.empty()) {
                    values[column_index] = parse_value(token.type, token.value, size);
                } else {
                    values[column_index] = std::nullopt;
                }
            } else {
                throw std::invalid_argument("Invalid value in INSERT statement: " + token.value);
            }

            token = lexer.get_next_token();
            if (token.type == TokenType::Comma) {
                token = lexer.get_next_token();
            }
            column_index++;
        }

        while (column_index < columns.size()) {
            if (columns[column_index].has_attribute(ColumnAttribute::AutoIncrement)) {
                column_index++;
                continue;
            }
            if (columns[column_index].get_default_value().has_value()) {
                values[column_index] = columns[column_index].get_default_value();
            } else {
                throw std::invalid_argument("Missing value for column: " + columns[column_index].get_name());
            }
            column_index++;
        }
    }

    return values;
}

ParsedQuery QueryParser::parse(const std::string& query) {
    bool valid = QueryParser::validate_query(query);
    if (!valid) {
        throw std::invalid_argument("Unbalanced parentheses or braces in 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 >> std::ws;
            if (iss.peek() == '(') {
                throw std::invalid_argument("Table name cannot be empty.");
            }

            iss >> pq.table_name;

            if (!isValidIdentifier(pq.table_name)) {
                throw std::invalid_argument("Invalid table name: " + pq.table_name);
            }
            if (reservedKeywords.count(pq.table_name)) {
                throw std::invalid_argument("Reserved keyword used as table name: " + 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, ';');
            columns_def = columns_def.substr(0, columns_def.size() - 1);

            std::vector<std::string> column_defs = split_columns(columns_def);

            for (const auto& col_def : column_defs) {
                Column column = parse_column_definition(col_def);
                if (!isValidIdentifier(column.get_name())) {
                    throw std::invalid_argument("Invalid column name: " + column.get_name());
                }
                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;

            if (!isValidIdentifier(pq.table_name)) {
                throw std::invalid_argument("Invalid table name: " + pq.table_name);
            }
            if (reservedKeywords.count(pq.table_name)) {
                throw std::invalid_argument("Reserved keyword used as table name: " + 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());
                if (!isValidIdentifier(col)) {
                    throw std::invalid_argument("Invalid column name: " + col);
                }
                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\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];

            if (!isValidIdentifier(table_name)) {
                throw std::invalid_argument("Invalid table name: " + table_name);
            }
            if (reservedKeywords.count(table_name)) {
                throw std::invalid_argument("Reserved keyword used as table name: " + table_name);
            }

            pq.table_name = table_name;

            if (!db_) {
                throw std::runtime_error("Database reference is not set in QueryParser.");
            }

            std::shared_ptr<memdb::core::Table> table;
            try {
                table = db_->get_table(table_name);
                const auto& columns = table->get_columns();
                pq.insert_values = parse_values(values_str, columns);
            } catch (exceptions::TableNotFoundException& e) {
                throw std::invalid_argument("Table does not exist: " + table_name);
            }
        }
        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];
            std::string table_name = matches[2];

            if (!isValidIdentifier(table_name)) {
                throw std::invalid_argument("Invalid table name: " + table_name);
            }
            if (reservedKeywords.count(table_name)) {
                throw std::invalid_argument("Reserved keyword used as table name: " + table_name);
            }

            pq.table_name = table_name;

            std::vector<std::string> column_defs = split_columns(columns_str);

            std::regex select_col_regex(R"(^([^\s,]+(?:\s+[^\s,]+)*)(?:\s+as\s+(\w+))?$)", std::regex::icase);

            for (const auto& col_def : column_defs) {
                std::smatch col_matches;
                if (!std::regex_match(col_def, col_matches, select_col_regex)) {
                    throw std::invalid_argument("Invalid SELECT column definition: " + col_def);
                }

                std::string expr_str = col_matches[1];
                std::string alias = col_matches[2].matched ? col_matches[2].str() : "";

                expr_str = trim(expr_str);
                alias = trim(alias.empty() ? expr_str : alias); 

                ExpressionParser expr_parser(expr_str);
                auto expression = expr_parser.parse_expression();

                ParsedQuery::SelectItem select_col;
                select_col.expression = std::move(expression);
                select_col.alias = alias;

                pq.select_items.emplace_back(std::move(select_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.");
        }

        std::string assignments_str;
        std::getline(iss, assignments_str, 'w'); 

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

            if (!isValidIdentifier(col_name)) {
                throw std::invalid_argument("Invalid column name: " + col_name);
            }
            if (reservedKeywords.count(col_name)) {
                throw std::invalid_argument("Reserved keyword used as column name: " + pq.table_name);
            }

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

        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;

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

    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::Int32);

    size_t start = 0;
    if (col_def[start] == '{') {
        size_t end = col_def.find('}', start);
        if (end == std::string::npos) {
            throw std::invalid_argument("Expected '}' for column attributes.");
        }
        std::string attrs_str = col_def.substr(start + 1, end - start - 1);
        std::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());
            
            std::transform(attr.begin(), attr.end(), attr.begin(), ::tolower);

            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 = trim(type_str);

        Type type;
        size_t size = 0;
        if (type_str.find("string[") != std::string::npos || type_str.find("bytes[") != std::string::npos) {
            size = std::stoul(type_str.substr(type_str.find('[') + 1,
                                        type_str.find(']') - type_str.find('[') - 1));
        }

        std::string default_val_str = col_def.substr(eq_pos + 1);
        default_val_str = trim(default_val_str);

        Lexer lexer(default_val_str);
        Token token = lexer.get_next_token();

        if (token.type == TokenType::EndOfInput) {
            throw std::invalid_argument("Invalid default value: " + default_val_str);
        }

        default_value = parse_value(token.type, token.value, size);
    } else {
        type_str = col_def.substr(colon_pos + 1);
        type_str = trim(type_str);
    }

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

    Type type;
    size_t size = 0;
    if (type_str.find("int32") != std::string::npos) {
        type = Type::Int32;
    }
    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);
    }

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

std::vector<std::string> QueryParser::split_columns(const std::string& columns_def) {
    std::vector<std::string> columns;
    int brace_level = 0;
    int paren_level = 0;
    std::string current;
    
    for (char ch : columns_def) {
        if (ch == '{') {
            brace_level++;
            current += ch;
        }
        else if (ch == '}') {
            if (brace_level == 0) {
                throw std::invalid_argument("Unbalanced braces in column definitions.");
            }
            brace_level--;
            current += ch;
        }
        else if (ch == '(') {
            paren_level++;
            current += ch;
        }
        else if (ch == ')') {
            if (paren_level == 0) {
                throw std::invalid_argument("Unbalanced parantheses in column definitions.");
            }
            paren_level--;
            current += ch;
        }
        else if (ch == ',' && brace_level == 0 && paren_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));
    }

    if (brace_level != 0 || paren_level != 0) {
        throw std::invalid_argument("Unbalanced parentheses or braces in column definitions.");
    }
    
    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();
    
    return (start < end ? std::string(start, end) : "");
}

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

#include <algorithm>
#include <iostream>
#include <iomanip>

namespace memdb {
namespace core {

void QueryResult::print(std::ostream& os) const {
    if (!success_) {
        os << "Error: " << error_message_ << "\n";
        return;
    }

    os << "Number of columns: " << columns_.size() << "\n";

    std::vector<size_t> col_widths;
    for (size_t i = 0; i < columns_.size(); ++i) {
        size_t name_length = columns_[i].get_name().length();
        size_t type_length = columns_[i].type.to_string().length();
        size_t max_width = std::max(name_length, type_length);
        
        for (const auto& row : data_) {
            if (i < row.size()) { 
                const auto& val = row[i];
                size_t val_length = val.has_value() ? val->to_string().length() : 4; // "NULL" имеет длину 4
                if (val_length > max_width) {
                    max_width = val_length;
                }
            }
        }
        col_widths.push_back(max_width + 2); 
    }

    auto print_separator = [&](const std::vector<size_t>& widths) {
        for (const auto& width : widths) {
            os << "+" << std::string(width + 2, '-');
        }
        os << "+\n";
    };

    print_separator(col_widths);

    os << "|";
    for (size_t i = 0; i < columns_.size(); ++i) {
        os << " " << std::left << std::setw(col_widths[i]) 
           << columns_[i].get_name() << " |";
    }
    os << "\n";

    os << "|";
    for (size_t i = 0; i < columns_.size(); ++i) {
        os << " " << std::left << std::setw(col_widths[i]) 
           << columns_[i].type.to_string() << " |";
    }
    os << "\n";

    print_separator(col_widths);

    for (const auto& row : data_) {
        os << "|";
        for (size_t i = 0; i < columns_.size(); ++i) {
            std::string value_str = "NULL";
            if (i < row.size() && row[i].has_value()) {
                value_str = row[i]->to_string();
            }
            if (value_str.length() > col_widths[i]) {
                value_str = value_str.substr(0, col_widths[i] - 3) + "...";
            }
            os << " " << std::left << std::setw(col_widths[i]) 
               << value_str << " |";
        }
        os << "\n";
    }

    print_separator(col_widths);
}

std::ostream& operator<<(std::ostream& os, const QueryResult& qr) {
    qr.print(os);
    return os;
}


QueryResultIterator QueryResult::begin() const {
    return QueryResultIterator(data_.cbegin(), columns_);
}

QueryResultIterator QueryResult::end() const {
    return QueryResultIterator(data_.cend(), columns_);
}

std::string QueryResult::to_string() const {
    if (!success_) return "Error: " + error_message_;
    
    std::ostringstream ss;
    std::vector<size_t> col_widths(columns_.size());

    for (size_t i = 0; i < columns_.size(); ++i) {
        col_widths[i] = std::max(
            columns_[i].name.length() + type_to_string(columns_[i].get_type()).length() + 3,
            get_max_value_width(i)
        );
    }

    print_separator(ss, col_widths);
    for (size_t i = 0; i < columns_.size(); ++i) {
        ss << "| " << std::left << std::setw(col_widths[i] - 2) 
           << columns_[i].name << " ";
    }
    ss << "|\n";

    for (size_t i = 0; i < columns_.size(); ++i) {
        ss << "| " << std::left << std::setw(col_widths[i] - 2) 
           << type_to_string(columns_[i].get_type()) << " ";
    }
    ss << "|\n";
    
    print_separator(ss, col_widths);

    for (const auto& row : data_) {
        for (size_t i = 0; i < row.size(); ++i) {
            ss << "| " << std::left << std::setw(col_widths[i] - 2)
               << (row[i].has_value() ? row[i]->to_string() : "NULL") << " ";
        }
        ss << "|\n";
    }
    
    print_separator(ss, col_widths);
    return ss.str();
}

void QueryResult::print_separator(std::ostringstream& ss, const std::vector<size_t>& widths) const {
    for (size_t width : widths) {
        ss << "+" << std::string(width, '-');
    }
    ss << "+\n";
}

size_t QueryResult::get_max_value_width(size_t col_idx) const {
    size_t max_width = 4; 
    for (const auto& row : data_) {
        if (row[col_idx].has_value()) {
            max_width = std::max(max_width, row[col_idx]->to_string().length());
        }
    }
    return max_width + 2;
}

std::string QueryResult::type_to_string(Type type) {
    switch (type) {
        case Type::Int32: return "int32";
        case Type::String: return "string";
        case Type::Bool: return "bool";
        case Type::Bytes: return "bytes";
        default: return "unknown";
    }
}

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

namespace memdb {
namespace core {

ResultRow QueryResultIterator::operator*() const {
    return ResultRow(*data_iter_, *columns_);
}

QueryResultIterator& QueryResultIterator::operator++() {
    ++data_iter_;
    return *this;
}

bool QueryResultIterator::operator!=(const QueryResultIterator& other) const {
    return data_iter_ != other.data_iter_;
}

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

namespace memdb {
namespace core {

ResultRow::ResultRow(const std::vector<std::optional<Value>>& values, const std::vector<ColumnInfo>& columns)
    : row_map_() 
{
    for (size_t i = 0; i < columns.size(); ++i) {
        if (values[i].has_value()) {
            row_map_[columns[i].name] = values[i].value();
        }
    }
}

const std::optional<Value>& ResultRow::operator[](const std::string& column_name) const {
    auto it = row_map_.find(column_name);
    if (it != row_map_.end()) {
        return it->second;
    }
    static std::optional<Value> empty;
    return empty;
}

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

namespace memdb {
namespace core {

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_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<int32_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<int32_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::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::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::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("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::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::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(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_) {
        return Type::Unknown;
    }
    return std::visit([](auto&& arg) -> Type {
        using T = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<T, int32_t>) {
            return Type::Int32;
        } else if constexpr (std::is_same_v<T, bool>) {
            return Type::Bool;
        } else if constexpr (std::is_same_v<T, std::string>) {
            return Type::String;
        } else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
            return Type::Bytes;
        } else {
            return Type::Unknown;
        }
    }, *data_);
}

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

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, 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, 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_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: // bool
            return std::get<bool>(*data_) ? "true" : "false";
        case 2: // std::string
            return "\"" + std::get<std::string>(*data_) + "\"";
        case 3: // 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
```
TestSelect.cpp
```
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "memdb/core/Database.h"
#include "memdb/core/QueryParser.h"

TEST(SelectTest, SelectAllColumns) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы
    std::string create_query = "create table users ({key, autoincrement} id : int32, name: string[32], age: int32, is_admin: bool = false);";
    memdb::core::ParsedQuery pq_create = parser.parse(create_query);
    db.create_table(pq_create.table_name, pq_create.columns);

    // Вставка данных
    std::string insert_query1 = "insert (1, \"Alice\", 30, true) to users;";
    std::string insert_query2 = "insert (2, \"Bob\", 25) to users;"; // is_admin по умолчанию false
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_insert1 = parser.parse(insert_query1);
        db.insert_row(pq_insert1.table_name, *pq_insert1.insert_values);

        memdb::core::ParsedQuery pq_insert2 = parser.parse(insert_query2);
        db.insert_row(pq_insert2.table_name, *pq_insert2.insert_values);
    });

    // Выполнение SELECT запроса
    std::string select_query = "select id, name, age, is_admin from users;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 2);

    // Проверка содержимого первой строки
    EXPECT_EQ(result.get_data()[0][0]->get_int(), 1);
    EXPECT_EQ(result.get_data()[0][1]->get_string(), "Alice");
    EXPECT_EQ(result.get_data()[0][2]->get_int(), 30);
    EXPECT_EQ(result.get_data()[0][3]->get_bool(), true);

    // Проверка содержимого второй строки
    EXPECT_EQ(result.get_data()[1][0]->get_int(), 2);
    EXPECT_EQ(result.get_data()[1][1]->get_string(), "Bob");
    EXPECT_EQ(result.get_data()[1][2]->get_int(), 25);
    EXPECT_EQ(result.get_data()[1][3]->get_bool(), false); // Значение по умолчанию
}

TEST(SelectTest, SelectSpecificColumns) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы
    std::string create_query = "create table employees ({key} emp_id : int32, first_name: string[20], last_name: string[20], salary: int32, department: string[30]);";
    memdb::core::ParsedQuery pq_create = parser.parse(create_query);
    db.create_table(pq_create.table_name, pq_create.columns);

    // Вставка данных
    std::vector<std::string> insert_queries = {
        "insert (101, \"John\", \"Doe\", 50000, \"Engineering\") to employees;",
        "insert (102, \"Jane\", \"Smith\", 60000, \"Marketing\") to employees;",
        "insert (103, \"Emily\", \"Jones\", 55000, \"Engineering\") to employees;"
    };
    for (const auto& query : insert_queries) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Выполнение SELECT запроса (только имя и отдел)
    std::string select_query = "select first_name, department from employees;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 3);

    // Проверка содержимого первой строки
    EXPECT_EQ(result.get_data()[0][0]->get_string(), "John");
    EXPECT_EQ(result.get_data()[0][1]->get_string(), "Engineering");

    // Проверка содержимого второй строки
    EXPECT_EQ(result.get_data()[1][0]->get_string(), "Jane");
    EXPECT_EQ(result.get_data()[1][1]->get_string(), "Marketing");

    // Проверка содержимого третьей строки
    EXPECT_EQ(result.get_data()[2][0]->get_string(), "Emily");
    EXPECT_EQ(result.get_data()[2][1]->get_string(), "Engineering");
}

TEST(SelectTest, SelectWithWhereConditionEquality) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы
    std::string create_query = "create table products ({key} product_id : int32, name: string[50], price: int32, in_stock: bool = true);";
    memdb::core::ParsedQuery pq_create = parser.parse(create_query);
    db.create_table(pq_create.table_name, pq_create.columns);

    // Вставка данных
    std::vector<std::string> insert_queries = {
        "insert (1, \"Laptop\", 1500, true) to products;",
        "insert (2, \"Smartphone\", 800) to products;", // in_stock по умолчанию true
        "insert (3, \"Tablet\", 600, false) to products;"
    };
    for (const auto& query : insert_queries) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Выполнение SELECT запроса с WHERE условием (price = 800)
    std::string select_query = "select product_id, name, price, in_stock from products where price = 800;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 1);

    // Проверка содержимого строки
    EXPECT_EQ(result.get_data()[0][0]->get_int(), 2);
    EXPECT_EQ(result.get_data()[0][1]->get_string(), "Smartphone");
    EXPECT_EQ(result.get_data()[0][2]->get_int(), 800);
    EXPECT_EQ(result.get_data()[0][3]->get_bool(), true); // Значение по умолчанию
}

TEST(SelectTest, SelectWithWhereConditionLogicalOperators) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы
    std::string create_query = "create table orders ({key} order_id : int32, customer: string[30], amount: int32, status: string[20]);";
    memdb::core::ParsedQuery pq_create = parser.parse(create_query);
    db.create_table(pq_create.table_name, pq_create.columns);

    // Вставка данных
    std::vector<std::string> insert_queries = {
        "insert (1001, \"Alice\", 250, \"shipped\") to orders;",
        "insert (1002, \"Bob\", 450, \"processing\") to orders;",
        "insert (1003, \"Charlie\", 150, \"shipped\") to orders;",
        "insert (1004, \"Diana\", 500, \"processing\") to orders;",
        "insert (1005, \"Eve\", 300, \"shipped\") to orders;"
    };
    for (const auto& query : insert_queries) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Выполнение SELECT запроса с условиями (amount > 200 AND status = "shipped")
    std::string select_query = "select order_id, customer, amount, status from orders where amount > 200 && status = \"shipped\";";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 2);

    // Проверка содержимого первой строки
    EXPECT_EQ(result.get_data()[0][0]->get_int(), 1001);
    EXPECT_EQ(result.get_data()[0][1]->get_string(), "Alice");
    EXPECT_EQ(result.get_data()[0][2]->get_int(), 250);
    EXPECT_EQ(result.get_data()[0][3]->get_string(), "shipped");

    // Проверка содержимого второй строки
    EXPECT_EQ(result.get_data()[1][0]->get_int(), 1005);
    EXPECT_EQ(result.get_data()[1][1]->get_string(), "Eve");
    EXPECT_EQ(result.get_data()[1][2]->get_int(), 300);
    EXPECT_EQ(result.get_data()[1][3]->get_string(), "shipped");
}

TEST(SelectTest, SelectLengthFunction) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы
    std::string create_query = "create table books ({key} book_id : int32, title: string[100], author: string[50]);";
    memdb::core::ParsedQuery pq_create = parser.parse(create_query);
    db.create_table(pq_create.table_name, pq_create.columns);

    // Вставка данных
    std::vector<std::string> insert_queries = {
        "insert (1, \"The Great Gatsby\", \"F. Scott Fitzgerald\") to books;",
        "insert (2, \"1984\", \"George Orwell\") to books;",
        "insert (3, \"To Kill a Mockingbird\", \"Harper Lee\") to books;"
    };
    for (const auto& query : insert_queries) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Выполнение SELECT запроса с использованием функции длины (|title|)
    std::string select_query = "select book_id, title, |title| as title_length from books where |title| > 10;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 2);

    // Проверка содержимого первой строки
    EXPECT_EQ(result.get_data()[0][0]->get_int(), 1);
    EXPECT_EQ(result.get_data()[0][1]->get_string(), "The Great Gatsby");
    EXPECT_EQ(result.get_data()[0][2]->get_int(), 16);

    // Проверка содержимого второй строки
    EXPECT_EQ(result.get_data()[1][0]->get_int(), 3);
    EXPECT_EQ(result.get_data()[1][1]->get_string(), "To Kill a Mockingbird");
    EXPECT_EQ(result.get_data()[1][2]->get_int(), 21);
}

TEST(SelectTest, SelectWithJoin) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы users
    std::string create_users = "create table users ({key, autoincrement} id : int32, name: string[30], department: string[20]);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_users);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Создание таблицы projects
    std::string create_projects = "create table projects ({key} project_id : int32, project_name: string[50], user_id: int32);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_projects);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Вставка данных в users
    std::vector<std::string> insert_users = {
        "insert (, \"Alice\", \"Engineering\") to users;",
        "insert (, \"Bob\", \"Marketing\") to users;",
        "insert (, \"Charlie\", \"Engineering\") to users;"
    };
    for (const auto& query : insert_users) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Вставка данных в projects
    std::vector<std::string> insert_projects = {
        "insert (101, \"Project Alpha\", 1) to projects;",
        "insert (102, \"Project Beta\", 2) to projects;",
        "insert (103, \"Project Gamma\", 1) to projects;",
        "insert (104, \"Project Delta\", 3) to projects;"
    };
    for (const auto& query : insert_projects) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Выполнение SELECT запроса с JOIN (users join projects on users.id = projects.user_id)
    std::string select_query = "select users.name, projects.project_name from users join projects on users.id = projects.user_id where users.department = \"Engineering\";";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 3); // Alice: 2 projects, Charlie: 1 project

    // Проверка содержимого первой строки
    EXPECT_EQ(result.get_data()[0][0]->get_string(), "Alice");
    EXPECT_EQ(result.get_data()[0][1]->get_string(), "Project Alpha");

    // Проверка содержимого второй строки
    EXPECT_EQ(result.get_data()[1][0]->get_string(), "Alice");
    EXPECT_EQ(result.get_data()[1][1]->get_string(), "Project Gamma");

    // Проверка содержимого третьей строки
    EXPECT_EQ(result.get_data()[2][0]->get_string(), "Charlie");
    EXPECT_EQ(result.get_data()[2][1]->get_string(), "Project Delta");
}

TEST(SelectTest, SelectFromEmptyTable) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание пустой таблицы
    std::string create_query = "create table empty_table (id : int32, description: string[50]);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_query);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Выполнение SELECT запроса
    std::string select_query = "select id, description from empty_table;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 0);
}

TEST(SelectTest, SelectWithNoMatchingRows) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы
    std::string create_query = "create table inventory (item_id : int32, item_name: string[30], quantity: int32);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_query);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Вставка данных
    std::vector<std::string> insert_queries = {
        "insert (1, \"Widget\", 100) to inventory;",
        "insert (2, \"Gadget\", 50) to inventory;",
        "insert (3, \"Thingamajig\", 0) to inventory;"
    };
    for (const auto& query : insert_queries) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Выполнение SELECT запроса с условием, которое не соответствует ни одной записи
    std::string select_query = "select item_id, item_name, quantity from inventory where quantity > 200;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 0);
}

TEST(SelectTest, SelectWithNotOperator) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы
    std::string create_query = "create table devices ({key} device_id : int32, device_name: string[30], active: bool);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_query);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Вставка данных
    std::vector<std::string> insert_queries = {
        "insert (1, \"Router\", true) to devices;",
        "insert (2, \"Switch\", false) to devices;",
        "insert (3, \"Firewall\", true) to devices;",
        "insert (4, \"Access Point\", false) to devices;"
    };
    for (const auto& query : insert_queries) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Выполнение SELECT запроса с условием NOT active
    std::string select_query = "select device_id, device_name from devices where !active;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 2);

    // Проверка содержимого первой строки
    EXPECT_EQ(result.get_data()[0][0]->get_int(), 2);
    EXPECT_EQ(result.get_data()[0][1]->get_string(), "Switch");

    // Проверка содержимого второй строки
    EXPECT_EQ(result.get_data()[1][0]->get_int(), 4);
    EXPECT_EQ(result.get_data()[1][1]->get_string(), "Access Point");
}

TEST(SelectTest, SelectWithMultipleLogicalOperators) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы
    std::string create_query = "create table sales ({key} sale_id : int32, product: string[30], quantity: int32, region: string[20]);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_query);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Вставка данных
    std::vector<std::string> insert_queries = {
        "insert (1, \"Laptop\", 5, \"North\") to sales;",
        "insert (2, \"Laptop\", 10, \"South\") to sales;",
        "insert (3, \"Smartphone\", 15, \"North\") to sales;",
        "insert (4, \"Tablet\", 20, \"East\") to sales;",
        "insert (5, \"Laptop\", 25, \"West\") to sales;"
    };
    for (const auto& query : insert_queries) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Выполнение SELECT запроса с условиями (product = "Laptop" AND quantity >= 10) OR region = "East"
    std::string select_query = "select sale_id, product, quantity, region from sales where (product = \"Laptop\" && quantity >= 10) || region = \"East\";";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 3);

    // Проверка содержимого первой строки
    EXPECT_EQ(result.get_data()[0][0]->get_int(), 2);
    EXPECT_EQ(result.get_data()[0][1]->get_string(), "Laptop");
    EXPECT_EQ(result.get_data()[0][2]->get_int(), 10);
    EXPECT_EQ(result.get_data()[0][3]->get_string(), "South");

    // Проверка содержимого второй строки
    EXPECT_EQ(result.get_data()[1][0]->get_int(), 4);
    EXPECT_EQ(result.get_data()[1][1]->get_string(), "Tablet");
    EXPECT_EQ(result.get_data()[1][2]->get_int(), 20);
    EXPECT_EQ(result.get_data()[1][3]->get_string(), "East");

    // Проверка содержимого третьей строки
    EXPECT_EQ(result.get_data()[2][0]->get_int(), 5);
    EXPECT_EQ(result.get_data()[2][1]->get_string(), "Laptop");
    EXPECT_EQ(result.get_data()[2][2]->get_int(), 25);
    EXPECT_EQ(result.get_data()[2][3]->get_string(), "West");
}

TEST(SelectTest, SelectWithLengthFunction) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы
    std::string create_query = "create table books ({key} book_id : int32, title: string[100], author: string[50]);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_query);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Вставка данных
    std::vector<std::string> insert_queries = {
        "insert (1, \"The Great Gatsby\", \"F. Scott Fitzgerald\") to books;",
        "insert (2, \"1984\", \"George Orwell\") to books;",
        "insert (3, \"To Kill a Mockingbird\", \"Harper Lee\") to books;"
    };
    for (const auto& query : insert_queries) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Выполнение SELECT запроса с функцией длины |title|
    std::string select_query = "select book_id, title, |title| as title_length from books where |title| > 10;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 2);

    // Проверка содержимого первой строки
    EXPECT_EQ(result.get_data()[0][0]->get_int(), 1);
    EXPECT_EQ(result.get_data()[0][1]->get_string(), "The Great Gatsby");
    EXPECT_EQ(result.get_data()[0][2]->get_int(), 16);

    // Проверка содержимого второй строки
    EXPECT_EQ(result.get_data()[1][0]->get_int(), 3);
    EXPECT_EQ(result.get_data()[1][1]->get_string(), "To Kill a Mockingbird");
    EXPECT_EQ(result.get_data()[1][2]->get_int(), 21);
}

TEST(SelectTest, SelectWithJoinAndWhere) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы customers
    std::string create_customers = "create table customers ({key, autoincrement} customer_id : int32, name: string[30], city: string[20]);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_customers);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Создание таблицы orders
    std::string create_orders = "create table orders ({key} order_id : int32, customer_id: int32, amount: int32);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_orders);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Вставка данных в customers
    std::vector<std::string> insert_customers = {
        "insert (, \"Alice\", \"New York\") to customers;",
        "insert (, \"Bob\", \"Los Angeles\") to customers;",
        "insert (, \"Charlie\", \"Chicago\") to customers;"
    };
    for (const auto& query : insert_customers) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Вставка данных в orders
    std::vector<std::string> insert_orders = {
        "insert (101, 1, 300) to orders;",
        "insert (102, 2, 200) to orders;",
        "insert (103, 1, 450) to orders;",
        "insert (104, 3, 500) to orders;"
    };
    for (const auto& query : insert_orders) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Выполнение SELECT запроса с JOIN и условием (amount > 300)
    std::string select_query = "select customers.name, orders.order_id, orders.amount from customers join orders on customers.customer_id = orders.customer_id where orders.amount > 300;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 2); // Alice: 2 orders (>300), Charlie: 1 order (500)

    // Исправление: Согласно условию orders.amount > 300, строка с amount = 300 не должна быть выбрана. Поэтому ожидается только orders 103 и 104
    ASSERT_EQ(result.get_data()[0][0]->get_string(), "Alice");
    ASSERT_EQ(result.get_data()[0][1]->get_int(), 103);
    ASSERT_EQ(result.get_data()[0][2]->get_int(), 450);

    // Проверка содержимого второй строки
    EXPECT_EQ(result.get_data()[1][0]->get_string(), "Charlie");
    EXPECT_EQ(result.get_data()[1][1]->get_int(), 104);
    EXPECT_EQ(result.get_data()[1][2]->get_int(), 500);
}

TEST(SelectTest, SelectFromEmptyJoinResult) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы customers
    std::string create_customers = "create table customers ({key, autoincrement} customer_id : int32, name: string[30], city: string[20]);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_customers);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Создание таблицы orders
    std::string create_orders = "create table orders ({key} order_id : int32, customer_id: int32, amount: int32);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_orders);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Вставка данных в customers
    std::vector<std::string> insert_customers = {
        "insert (1, \"Alice\", \"New York\") to customers;",
        "insert (2, \"Bob\", \"Los Angeles\") to customers;"
    };
    for (const auto& query : insert_customers) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Вставка данных в orders с отсутствующими customer_id
    std::vector<std::string> insert_orders = {
        "insert (101, 3, 300) to orders;", // customer_id 3 не существует
        "insert (102, 4, 200) to orders;"  // customer_id 4 не существует
    };
    for (const auto& query : insert_orders) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Выполнение SELECT запроса с JOIN, который не найдет соответствий
    std::string select_query = "select customers.name, orders.order_id, orders.amount from customers join orders on customers.customer_id = orders.customer_id where customers.city = \"Chicago\";";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 0);
}

TEST(SelectTest, SelectWithAggregateFunctions) {
    // *Внимание:* Согласно вашему описанию задачи, агрегатные функции не были упомянуты и, вероятно, не реализованы.
    // Поэтому этот тест только для примера и, возможно, не будет работать до реализации соответствующей функциональности.

    /*
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы sales
    std::string create_sales = "create table sales ({key} sale_id : int32, product: string[30], quantity: int32, price: int32);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_sales);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Вставка данных
    std::vector<std::string> insert_sales = {
        "insert (1, \"Laptop\", 5, 1000) to sales;",
        "insert (2, \"Smartphone\", 10, 500) to sales;",
        "insert (3, \"Tablet\", 7, 750) to sales;"
    };
    for (const auto& query : insert_sales) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Выполнение SELECT запроса с агрегатной функцией SUM(quantity)
    std::string select_query = "select product, SUM(quantity) as total_quantity from sales group by product;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка содержимого результатов
    // Ожидаем:
    // Laptop | 5
    // Smartphone | 10
    // Tablet | 7

    ASSERT_EQ(result.get_data().size(), 3);
    EXPECT_EQ(result.get_data()[0][0]->get_string(), "Laptop");
    EXPECT_EQ(result.get_data()[0][1]->get_int(), 5);
    EXPECT_EQ(result.get_data()[1][0]->get_string(), "Smartphone");
    EXPECT_EQ(result.get_data()[1][1]->get_int(), 10);
    EXPECT_EQ(result.get_data()[2][0]->get_string(), "Tablet");
    EXPECT_EQ(result.get_data()[2][1]->get_int(), 7);
    */
}

TEST(SelectTest, SelectFromNonExistentTable) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Выполнение SELECT запроса из несуществующей таблицы
    std::string select_query = "select id, name from nonexistent_table;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка неуспешности выполнения
    ASSERT_FALSE(result.is_ok());

    // Проверка сообщения об ошибке
    EXPECT_THAT(result.get_error(), ::testing::HasSubstr("Table not found: nonexistent_table"));
}

TEST(SelectTest, SelectFromNonExistentColumn) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы
    std::string create_query = "create table inventory (item_id : int32, item_name: string[30], quantity: int32);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_query);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Вставка данных
    std::string insert_query = "insert (1, \"Widget\", 100) to inventory;";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_insert = parser.parse(insert_query);
        db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
    });

    // Выполнение SELECT запроса с несуществующим столбцом
    std::string select_query = "select item_id, price from inventory;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка неуспешности выполнения
    ASSERT_FALSE(result.is_ok());

    // Проверка сообщения об ошибке
    EXPECT_THAT(result.get_error(), ::testing::HasSubstr("Column not found: price"));
}

TEST(SelectTest, SelectWithExpressionsInColumns) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы accounts
    std::string create_query = "create table accounts ({key} account_id : int32, balance: int32);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_query);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Вставка данных
    std::vector<std::string> insert_queries = {
        "insert (1, 1000) to accounts;",
        "insert (2, 2000) to accounts;",
        "insert (3, 3000) to accounts;"
    };
    for (const auto& query : insert_queries) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Выполнение SELECT запроса с выражением (balance * 2) as double_balance
    std::string select_query = "select account_id, balance, balance * 2 as double_balance from accounts;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 3);

    // Проверка содержимого первой строки
    EXPECT_EQ(result.get_data()[0][0]->get_int(), 1);
    EXPECT_EQ(result.get_data()[0][1]->get_int(), 1000);
    EXPECT_EQ(result.get_data()[0][2]->get_int(), 2000);

    // Проверка содержимого второй строки
    EXPECT_EQ(result.get_data()[1][0]->get_int(), 2);
    EXPECT_EQ(result.get_data()[1][1]->get_int(), 2000);
    EXPECT_EQ(result.get_data()[1][2]->get_int(), 4000);

    // Проверка содержимого третьей строки
    EXPECT_EQ(result.get_data()[2][0]->get_int(), 3);
    EXPECT_EQ(result.get_data()[2][1]->get_int(), 3000);
    EXPECT_EQ(result.get_data()[2][2]->get_int(), 6000);
}

TEST(SelectTest, SelectWithJoinAndNoMatchingRows) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы authors
    std::string create_authors = "create table authors ({key, autoincrement} author_id : int32, name: string[30]);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_authors);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Создание таблицы books
    std::string create_books = "create table books ({key} book_id : int32, title: string[50], author_id: int32);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_books);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Вставка данных в authors
    std::vector<std::string> insert_authors = {
        "insert (, \"George Orwell\") to authors;",
        "insert (, \"J.K. Rowling\") to authors;"
    };
    for (const auto& query : insert_authors) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Вставка данных в books
    std::vector<std::string> insert_books = {
        "insert (101, \"1984\", 1) to books;",
        "insert (102, \"Harry Potter\", 2) to books;",
        "insert (103, \"Animal Farm\", 1) to books;"
    };
    for (const auto& query : insert_books) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Выполнение SELECT запроса с JOIN и условием, которое не соответствует ни одной записи
    std::string select_query = "select authors.name, books.title from authors join books on authors.author_id = books.author_id where authors.name = \"Unknown\";";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 0);
}

TEST(SelectTest, SelectWithInvalidSelectStatement) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы
    std::string create_query = "create table inventory (item_id : int32, item_name: string[30], quantity: int32);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_query);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Вставка данных
    std::string insert_query = "insert (1, \"Widget\", 100) to inventory;";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_insert = parser.parse(insert_query);
        db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
    });

    // Выполнение некорректного SELECT запроса (несуществующий столбец)
    std::string select_query = "select item_id, price from inventory;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка неуспешности выполнения
    ASSERT_FALSE(result.is_ok());

    // Проверка сообщения об ошибке
    EXPECT_THAT(result.get_error(), ::testing::HasSubstr("Column not found: price"));
}

TEST(SelectTest, SelectWithExpressionInColumns) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы metrics
    std::string create_query = "create table metrics ({key} metric_id : int32, value1: int32, value2: int32);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_query);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Вставка данных
    std::string insert_query1 = "insert (1, 10, 20) to metrics;";
    std::string insert_query2 = "insert (2, 15, 25) to metrics;";
    std::string insert_query3 = "insert (3, 20, 30) to metrics;";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_insert1 = parser.parse(insert_query1);
        db.insert_row(pq_insert1.table_name, *pq_insert1.insert_values);

        memdb::core::ParsedQuery pq_insert2 = parser.parse(insert_query2);
        db.insert_row(pq_insert2.table_name, *pq_insert2.insert_values);

        memdb::core::ParsedQuery pq_insert3 = parser.parse(insert_query3);
        db.insert_row(pq_insert3.table_name, *pq_insert3.insert_values);
    });

    // Выполнение SELECT запроса с выражением (value1 + value2) as total
    std::string select_query = "select metric_id, value1, value2, value1 + value2 as total from metrics;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Проверка количества возвращённых строк
    ASSERT_EQ(result.get_data().size(), 3);

    // Проверка содержимого первой строки
    EXPECT_EQ(result.get_data()[0][0]->get_int(), 1);
    EXPECT_EQ(result.get_data()[0][1]->get_int(), 10);
    EXPECT_EQ(result.get_data()[0][2]->get_int(), 20);
    EXPECT_EQ(result.get_data()[0][3]->get_int(), 30);

    // Проверка содержимого второй строки
    EXPECT_EQ(result.get_data()[1][0]->get_int(), 2);
    EXPECT_EQ(result.get_data()[1][1]->get_int(), 15);
    EXPECT_EQ(result.get_data()[1][2]->get_int(), 25);
    EXPECT_EQ(result.get_data()[1][3]->get_int(), 40);

    // Проверка содержимого третьей строки
    EXPECT_EQ(result.get_data()[2][0]->get_int(), 3);
    EXPECT_EQ(result.get_data()[2][1]->get_int(), 20);
    EXPECT_EQ(result.get_data()[2][2]->get_int(), 30);
    EXPECT_EQ(result.get_data()[2][3]->get_int(), 50);
}

TEST(SelectTest, SelectWithInvalidDataTypesInExpressions) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы types
    std::string create_query = "create table types ({key} type_id : int32, name: string[30], flag: bool);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_query);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Вставка данных
    std::string insert_query1 = "insert (1, \"TypeA\", true) to types;";
    std::string insert_query2 = "insert (2, \"TypeB\", false) to types;";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_insert1 = parser.parse(insert_query1);
        db.insert_row(pq_insert1.table_name, *pq_insert1.insert_values);

        memdb::core::ParsedQuery pq_insert2 = parser.parse(insert_query2);
        db.insert_row(pq_insert2.table_name, *pq_insert2.insert_values);
    });

    // Выполнение некорректного SELECT запроса с выражением (name + flag)
    std::string select_query = "select type_id, name, flag, name + flag as invalid_expression from types;";
    memdb::core::QueryResult result = db.execute(select_query);

    // Проверка неуспешности выполнения
    ASSERT_FALSE(result.is_ok());

    // Проверка сообщения об ошибке (типы данных несовместимы для операции '+')
    EXPECT_THAT(result.get_error(), ::testing::HasSubstr("Operator '+' not supported for given types."));
}

TEST(SelectTest, SelectWithJoinAndMultipleConditions) {
    memdb::core::Database db;
    memdb::core::QueryParser parser;
    parser.set_database(&db);

    // Создание таблицы departments
    std::string create_departments = "create table departments ({key} dept_id : int32, dept_name: string[30]);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_departments);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Создание таблицы employees
    std::string create_employees = "create table employees ({key, autoincrement} emp_id : int32, name: string[30], salary: int32, dept_id: int32, active: bool = true);";
    ASSERT_NO_THROW({
        memdb::core::ParsedQuery pq_create = parser.parse(create_employees);
        db.create_table(pq_create.table_name, pq_create.columns);
    });

    // Вставка данных в departments
    std::vector<std::string> insert_departments = {
        "insert (1, \"Engineering\") to departments;",
        "insert (2, \"Marketing\") to departments;",
        "insert (3, \"Sales\") to departments;"
    };
    for (const auto& query : insert_departments) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Вставка данных в employees
    std::vector<std::string> insert_employees = {
        "insert (, \"Alice\", 70000, 1, true) to employees;",
        "insert (, \"Bob\", 50000, 1) to employees;",
        "insert (, \"Charlie\", 60000, 2, false) to employees;",
        "insert (, \"David\", 55000, 1) to employees;"
    };
    for (const auto& query : insert_employees) {
        ASSERT_NO_THROW({
            memdb::core::ParsedQuery pq_insert = parser.parse(query);
            db.insert_row(pq_insert.table_name, *pq_insert.insert_values);
        });
    }

    // Проверка количества строк в employees
    auto employees_table = db.get_table("employees");
    ASSERT_EQ(employees_table->get_all_rows().size(), 4);

    // Выполнение SELECT запроса с JOIN и множественными условиями
    std::string select_query = "select employees.emp_id, employees.name, departments.dept_name, employees.salary from employees join departments on employees.dept_id = departments.dept_id where departments.dept_name = \"Engineering\" && employees.salary > 60000 || employees.active = false;";
    memdb::core::QueryResult result = db.execute(select_query);

    result.print();

    // Проверка успешности выполнения
    ASSERT_TRUE(result.is_ok());

    // Ожидаемые строки:
    // - Bob: Engineering, 50000 (salary <= 60000, active = true) -> Не подходит
    // - Charlie: Marketing, 60000, active = false -> Подходит
    // - David: Engineering, 55000 -> Не подходит
    // Итого: только Charlie
    ASSERT_EQ(result.get_data().size(), 1);

    // Проверка содержимого строки
    EXPECT_EQ(result.get_data()[0][0]->get_int(), 3);
    EXPECT_EQ(result.get_data()[0][1]->get_string(), "Charlie");
    EXPECT_EQ(result.get_data()[0][2]->get_string(), "Marketing");
    EXPECT_EQ(result.get_data()[0][3]->get_int(), 60000);
}
```
```
[ RUN      ] SelectTest.SelectWithJoinAndMultipleConditions
4 3
Number of columns: 7
+--------------------+------------------+--------------------+---------------------+--------------------+-----------------------+-------------------------+
| employees.emp_id   | employees.name   | employees.salary   | employees.dept_id   | employees.active   | departments.dept_id   | departments.dept_name   |
| int32              | string[30]       | int32              | int32               | bool               | int32                 | string[30]              |
+--------------------+------------------+--------------------+---------------------+--------------------+-----------------------+-------------------------+
| 1                  | "Alice"          | "Engineering"      | 70000               | NULL               | NULL                  | NULL                    |
| 3                  | "Charlie"        | "Marketing"        | 60000               | NULL               | NULL                  | NULL                    |
+--------------------+------------------+--------------------+---------------------+--------------------+-----------------------+-------------------------+
/home/kirill/Desktop/memdb_project2/memdb_project/tests/test_core/TestSelect.cpp:1012: Failure
Expected equality of these values:
  result.get_data().size()
    Which is: 2
  1

[  FAILED  ] SelectTest.SelectWithJoinAndMultipleConditions (5 ms)
[----------] 22 tests from SelectTest (75 ms total)

[----------] Global test environment tear-down
[==========] 70 tests from 3 test suites ran. (153 ms total)
[  PASSED  ] 69 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] SelectTest.SelectWithJoinAndMultipleConditions

 1 FAILED TEST
```
это нормально, что у меня стоят null вместо значений? да и в целом значения поехали, написано, что зарплата - это строчки "Engineering" и "Marketing". как это править? сама ошибка теста в неправильности теста, я это понимаю. не понимаю, что не так с функцией print()