Данный документ описывает архитектуру, функциональные возможности и внутреннее устройство реляционной системы управления базами данных databasetopit. Разделы выстроены в хронологическом порядке, отражающем жизненный цикл обработки запроса: от сетевого взаимодействия до физической записи на диск.

1. Взаимодействие с сервером и API

Жизненный цикл любого запроса начинается на сетевом уровне. Система спроектирована по клиент-серверной архитектуре на основе протокола HTTP:

  • REST API и JSON: HTTP-сервер (реализованный на базе cpp-httplib) общается с клиентами, принимая и отправляя данные в формате JSON. Запросы маршрутизируются через эндпоинты /query, /login, /register, /text2sql и др.
  • Сетевые настройки и IP-адресация: Сервер может быть запущен как локально (на localhost/127.0.0.1 с портом по умолчанию 8080), так и на внешнем IP-адресе (например, 0.0.0.0 или публичном IP) для обслуживания удаленных клиентов по локальной сети или интернету.
  • Управление сессиями (State Management): Несмотря на то что HTTP является протоколом без сохранения состояния (stateless), сервер поддерживает строгий механизм сессий. При успешном логине клиенту возвращается уникальный session_id, который кэшируется в unordered_map в связке с текущим контекстом (current_user, current_db). Этот токен используется во всех последующих вызовах для прозрачной идентификации состояния.

2. Интеллектуальный транслятор Text2SQL

Если пользователь формулирует запрос на естественном языке, он перехватывается ИИ-модулем до этапа классического парсинга:

  • Сбор контекста: Клиент отправляет текстовый запрос на специализированный эндпоинт /text2sql. Сервер динамически агрегирует схему текущей базы данных пользователя (состав таблиц, связи и типы колонок) и формирует структурированный промпт.
  • Взаимодействие с ИИ: Сформированный промпт отправляется по защищенному каналу HTTPS к Mistral AI API (модель mistral-small-latest). В ответ система получает JSON с готовым SQL-запросом, адаптированным под конкретную структуру БД.
  • Безопасность конфигурации: Ключ авторизации API (MISTRAL_API_KEY) инжектируется строго через переменные окружения на стороне сервера. Это исключает компрометацию ключей на стороне клиентов (GUI).

3. Парсинг и синтаксис SQL (AST)

Полученный SQL-запрос проходит стадию лексического и синтаксического анализа:

  • Абстрактное синтаксическое дерево (AST): Модуль синтаксического анализа четко разделен на Lexer (разбиение запроса на токены) и Parser. Парсер не выполняет запросы на лету, а строит AST (дерево выражений, например SelectStatement, BinaryExpression). Это архитектурно отделяет этап парсинга от этапа выполнения (в подсистеме Executor).
  • Рекурсивный вычислитель выражений (Evaluation Engine): Движок Executor содержит встроенный рекурсивный интерпретатор, способный вычислять ветви AST любой степени вложенности. Он поддерживает ленивое вычисление (short-circuiting) для логических операторов AND и OR и позволяет комбинировать математику, подзапросы и агрегации в сложных конструкциях (например, WHERE (a + b * 2) > (SELECT COUNT(*) FROM X)).
  • DDL: Поддержка создания таблиц с ограничениями PRIMARY KEY, NOT NULL, UNIQUE, DEFAULT, FOREIGN KEY (REFERENCES ... ON DELETE/UPDATE CASCADE/SET NULL). Реализован механизм генерации суррогатных ключей AUTOINCREMENT. Поддержка изменения структуры через ALTER TABLE (ADD/DROP COLUMN).
  • Инспекция метаданных (SHOW): Реализовано семейство команд SHOW (DATABASES, TABLES, COLUMNS, INDEX). Особая возможность — SHOW CREATE TABLE, реконструирующая валидный DDL-запрос "на лету" из бинарной схемы таблицы.
  • DQL / DML: Продвинутая фильтрация с предикатами LIKE, BETWEEN, IS NULL, IN. Поддержка коррелированных подзапросов, конструкций IN (SELECT ...) и EXISTS (SELECT ...). Поддержка математических операций.
  • Динамическая CURRENT_DATE: Встроена поддержка функции CURRENT_DATE(), которую можно использовать как в выборках, так и в качестве значения по умолчанию (DEFAULT CURRENT_DATE). Движок динамически генерирует строку YYYY-MM-DD при INSERT.
  • Агрегация и защита (Resource Guard): Вычисление COUNT, SUM, AVG, MIN, MAX с использованием GROUP BY и HAVING. Сортировка ORDER BY, пагинация (LIMIT, OFFSET). Для защиты памяти от перегрузки любой SELECT без явного указания лимита аппаратно ограничивается 100 записями (Default LIMIT 100).

4. Многопоточность и конкурентный доступ

Перед тем как движок начнет выполнять построенное AST-дерево, запрос проходит через подсистему блокировок:

  • Изоляция потоков: HTTP-сервер обрабатывает входящие запросы параллельно в пуле потоков. Изоляция транзакционного контекста сессий обеспечивается за счет thread_local переменных (таких как состояние транзакции txn_active_ и её ID current_txn_id_).
  • Управление блокировками: На уровне движка реализован механизм конкурентного доступа с использованием std::shared_mutex (db_rw_mutex_). Читающие транзакции (SELECT, SHOW) захватывают разделяемую блокировку (shared lock), позволяя выполнять запросы параллельно. Модифицирующие запросы захватывают эксклюзивную блокировку (exclusive lock), что предотвращает состояние гонки и обеспечивает сериализуемость доступа к общим ресурсам.

5. Ролевая модель и управление доступом (RBAC)

Получив необходимые права (блокировки) на доступ к структурам, движок валидирует права самого инициатора запроса:

  • Секьюритизация данных: Пароли подвергаются одностороннему хэшированию алгоритмом SHA-256 (с применением криптографического модуля OpenSSL).
  • Модель Ownership: Авторизация базируется на модели владения. Пользователь, создавший базу данных, регистрируется как её владелец и обладает полным спектром DDL и DML прав.
  • Делегирование прав и роль Editor: Владельцы имеют возможность предоставлять доступ другим аккаунтам посредством команд GRANT DDL ON <db> TO <user> и отзывать через REVOKE DDL. По умолчанию выдача прав назначает пользователю системную роль editor, которая позволяет выполнять любые модификации схемы данных и DML-запросы в рамках указанной базы, но не позволяет удалять саму базу данных (что доступно только владельцу или глобальному admin).
  • Self-hosting системных таблиц: Вся ролевая модель и метаданные (учетные записи sys_users, владельцы sys_db_owners, выданные права sys_db_grants) не хранятся в отдельных конфигурационных файлах. Они хранятся в точно таких же B+ деревьях внутри скрытой системной базы system. На них автоматически распространяются все гарантии ACID, Buffer Pool кэширование и механизм восстановления через WAL.
  • Строгая изоляция системного каталога: Несмотря на то, что метаданные лежат в обычных таблицах, обычный пользователь (даже с высокими привилегиями) не может выполнить команды вроде DROP TABLE sys_users. На уровне ядра (Executor) реализована жесткая программная проверка: любые прямые DML и DDL модификации к базе system отклоняются с ошибкой доступа. Эта база модифицируется исключительно внутренними C++ процедурами при вызове команд управления (например, REGISTER, GRANT).

6. Архитектура хранилища и индексы

После прохождения проверок доступа начинается физическое исполнение запроса к данным:

  • Постраничное хранение и точечные обновления (In-place Updates): В отличие от базовых файловых хранилищ, переписывающих файл целиком при модификации, данные здесь организованы в бинарные .db файлы, разбитые на независимые страницы фиксированного размера (8 КБ). При выполнении UPDATE или `INSERT` система считывает, модифицирует и перезаписывает на диск только одну конкретную 8-килобайтную страницу, абсолютно не затрагивая остальной объем файла. Модуль BufferPool дополнительно кэширует эти страницы по алгоритму LRU, чтобы минимизировать физические обращения к диску.
  • Самоописываемые таблицы (Meta-page 0): Нулевая страница (Page 0) каждого бинарного файла зарезервирована под метаданные. Она хранит ID корневого узла B-дерева и саму схему таблицы (бинарно сериализованный объект TableSchema). Благодаря этому каждый файл является самоописываемым (self-describing) — структуру колонок можно восстановить напрямую из файла данных без внешних словарей.
  • Механизм переиспользования памяти (Freelist): Для предотвращения бесконечного роста файлов на диске реализована система управления свободными страницами. При удалении данных пустые страницы не теряются, а заносятся в Freelist. При последующих вставках движок приоритетно переиспользует эти пустые участки внутри существующего .db файла.
  • Структура Slotted Page и бинарный поиск: Линейное сканирование строк внутри 8-килобайтной страницы является неэффективным. Для оптимизации поиска реализован паттерн Slotted Page: данные добавляются от начала страницы к центру, а массив слотов (указателей) — от конца к центру. Массив слотов упорядочен по ключу, что позволяет использовать бинарный поиск (O(log K)) внутри загруженной в память страницы, значительно повышая производительность поиска.
  • Кластеризованные индексы (B+ Tree): Физическое расположение строк в таблицах упорядочено согласно структуре B+ дерева по первичному ключу. Это гарантирует логарифмическую временную сложность O(log N) для операций точечного поиска, вставки и удаления.
  • Вторичные индексы и составные ключи: Механизм CREATE INDEX позволяет создавать некластеризованные индексы. Для решения проблемы неуникальных значений (когда под один ключ во вторичном индексе попадает множество строк), вторичные индексы на системном уровне используют составной ключ вида [Значение_индекса, Primary_Key]. Поскольку Primary Key уникален, составной ключ также всегда уникален, что позволяет эффективно хранить любое количество дубликатов.
  • Оптимизатор Index Scan: Ядро выполнения запросов (tryIndexScan) автоматически определяет возможность использования B+ дерева при наличии предикатов WHERE col = 'val' или BETWEEN. При совпадении условий полное сканирование таблицы (Full Table Scan) заменяется на индексный поиск.
  • Оптимизация ORDER BY: Если в запросе присутствует ORDER BY по индексированной колонке, подсистема Executor избегает ресурсоемкой сортировки в памяти (in-memory sort). Движок просто последовательно читает листовые узлы B+ дерева, снижая алгоритмическую сложность сортировки с O(N log N) до O(N).
  • Механизмы JOIN: В зависимости от наличия индексов на объединяемых колонках, подсистема Executor маршрутизирует выполнение соединения. При совпадении с PK или вторичным индексом применяется Index-Based Join. В остальных случаях (для equi-joins) используется Hash Join. Поддерживаются алгоритмы INNER, LEFT, RIGHT, FULL и CROSS.

7. Интеграция с CSV форматом

Один из специализированных видов запросов — пакетный импорт (обрабатывается так же через движок хранилища):

  • Команда LOAD CSV: Позволяет загружать данные из файла (синтаксис LOAD CSV 'path' INTO table [APPEND]). Парсер построчно считывает файл, выполняет приведение типов и проверку всех существующих ограничений (Constraints). Каждая добавленная строка маршрутизируется через механизм WAL для гарантии сохранения данных при сбое.
  • Расширение схемы через CSV: Команда ALTER TABLE ADD COLUMN ... FROM CSV 'path' предоставляет механизм массового пополнения данных. Она позволяет добавить новый столбец в существующую таблицу и автоматически заполнить его значениями из CSV-файла, сопоставляя строки по первичному ключу. Механизм строго контролирует кардинальность и целостность, откатывая операцию при несовпадении ключей.

8. Типизация данных и формат хранения (CellValue / BLOB)

Физическое чтение и запись строк на страницы памяти строго типизированы:

  • Тип CellValue: Значения ячеек представлены специальным классом CellValue, реализованным как std::optional<CellPrimitive> (где CellPrimitive — это std::variant<int64_t, double, bool, std::string>). Такая архитектура позволяет элегантно обрабатывать NULL значения (как пустой std::optional) отдельно от пустых строк, а также обеспечивает type-safe операции и быстрые сравнения на уровне нативных типов C++ (int64_t, double) при сортировках и обходе B-деревьев.
  • Хранение BLOB и строк: Строковые значения (TEXT, VARCHAR) и бинарные данные переменной длины сериализуются модулем row_codec. Такие поля динамически упаковываются в конец бинарного представления записи, а в её фиксированной части сохраняются массивы смещений (offset arrays). Это обеспечивает компактное хранение на страницах и предоставляет быстрый O(1) доступ к любым колонкам (включая тяжелые BLOB-объекты) без необходимости десериализации всей строки при чтении.

9. Гарантии ACID и логирование (WAL)

Завершающий этап жизненного цикла транзакции — обеспечение надежности и сброс данных на диск:

  • Atomicity и Durability: Для обеспечения атомарности и отказоустойчивости применяется механизм Write-Ahead Logging (WAL). Любые модификации строк предварительно фиксируются в логе перед изменением страниц данных. При вызове COMMIT (или при достижении порога лога в 4 МБ) происходит принудительная синхронизация (fsync) WAL с диском.
  • Политика No-Force (Отложенная запись): Измененные данные 8-килобайтных страниц не записываются на диск сразу. Они помечаются в Buffer Pool как dirty. На диск синхронизируется исключительно легковесный WAL-лог. Тяжелая бинарная страница будет сброшена на диск только тогда, когда кэш переполнится и её вытеснит алгоритм LRU. Это кардинально снижает износ диска и существенно повышает пропускную способность для UPDATE-запросов.
  • Восстановление (Crash Recovery) и Откат (Undo): При инициализации сервера система считывает WAL и выполняет накат изменений (Redo). Для команды ROLLBACK реализована сложная логическая отмена (Undo). При откате движок читает записи WAL в обратном порядке от конца к началу транзакции, игнорируя уже зафиксированные (COMMIT) или прерванные (ABORT) транзакции. Для каждой операции модификации генерируется и немедленно пишется в WAL компенсационная запись (Compensation Log Record — CLR_ROW_UPSERT, CLR_ROW_DELETE), после чего физические данные возвращаются к состоянию до начала транзакции. Это гарантирует целостность базы даже в случае аппаратного сбоя во время выполнения отката.
  • Consistency: Обеспечивается строгим контролем ссылочной целостности и ограничений столбцов (FOREIGN KEY, UNIQUE, NOT NULL) в момент транзакции.

10. Клиентская часть и графический интерфейс (GUI)

Основной точкой взаимодействия пользователя с СУБД является полноценное кроссплатформенное десктопное приложение CaseChampGui, написанное на C# (Avalonia UI) с использованием архитектурного паттерна MVVM. Интерфейс разделен на логические модули и предоставляет следующие возможности:

  • Умная авторизация и управление профилем:
    • Экран входа поддерживает аутентификацию на удаленном сервере и создание локальных учетных записей. Есть функция кэширования сессий («Запомнить пароль на этом компьютере»).
    • В настройках аккаунта пользователь может загрузить собственный аватар, задать отображаемое имя (псевдоним для UI) и безопасно изменить пароль на сервере.
  • Настройка сетевого подключения:
    • Специализированный раздел настроек (вызываемый через иконку на экране приветствия) позволяет переопределить целевой IP-адрес и порт. Это дает возможность GUI клиенту гибко переключаться между локальной СУБД для тестирования и удаленными production-узлами.
  • Автозапуск сервера (Built-in Launcher):
    • В настройках подключения встроен механизм автоматического пинга сервера при старте. Если удаленный или локальный dbserver не отвечает, GUI способен автоматически запустить бинарный файл СУБД в фоновом режиме прямо рядом с проектом, избавляя от необходимости работы с терминалом.
  • Рабочее пространство SQL (Консоль):
    • Многострочный редактор с визуальным выбором active базы данных в верхнем меню (автоматически выполняет USE db).
    • Наличие кнопок истории запросов и функции Dry run (холостой прогон для безопасной проверки синтаксиса без изменения данных).
    • Справа располагается Динамический обозреватель схемы (Schema Viewer): он в виде раскрывающихся списков показывает структуру таблиц и колонок текущей БД, обеспечивая контекст при написании кода.
  • Визуализатор данных (Таблицы):
    • Отдельная вкладка для табличного просмотра (Read-only режим).
    • Интерфейс динамически генерирует DataGrid-колонки (например, employees.full_name, employees.salary) на основе неструктурированных JSON-ответов сервера.
    • Встроена удобная навигация: переключатель таблиц в нижней панели и элементы управления пагинацией (постраничный просмотр), а также кнопки обновления данных.
    • Управление фильтрами: Пользователи могут устанавливать фильтры для отдельных столбцов через всплывающее меню. Сбросить active фильтры можно в один клик с помощью кнопки «х фильтры» в нижней панели управления.
    • Два режима фильтрации:
      • Классический: использование стандартных операторов сравнения (=, >, <).
      • Text2SQL: позволяет ввести условие фильтрации на естественном русском языке (например, "старше 18 лет"). ИИ Mistral автоматически переведет этот текст в корректное выражение WHERE для текущей таблицы.
  • Интеллектуальный помощник (Text2SQL):
    • Выделенное окно для перевода естественного языка в запросы. Интерфейс показывает текущую схему базы, позволяя формулировать запросы с учетом контекста схемы (например, "покажи имена всех пользователей старше 25 лет").
    • Кнопка «Перевести и выполнить» отправляет текст к ИИ и маршрутизирует сгенерированный SQL прямо в ядро БД.
  • Продвинутое управление ключами ИИ:
    • Пользователь настраивает стратегию ключей Mistral AI: можно использовать личный ключ, раздавать его другим локальным профилям на ПК (Share-режим) или принимать общие ключи.
  • Инновационный Чат-режим SQL (Chat Mode):
    • Специальный переключатель в настройках полностью меняет парадигму рабочей среды, превращая классическую SQL-консоль в интерфейс мессенджера.
    • Запросы пользователя отправляются через строку ввода внизу экрана и отображаются в виде "пузырей" (bubbles) с правой стороны.
    • Ответы базы данных появляются слева в хронологическом порядке с указанием времени (таймстемпами). Ошибки выводятся обычным текстом, а при успешном выполнении SELECT-запроса прямо внутри ответного сообщения рендерится полноценная встроенная DataGrid-таблица с результатами выборки.
  • Кастомизация и внешний вид:
    • Приложение поддерживает смену системных тем оформления и детальную настройку UI: размер шрифта редактора (через слайдер), включение плавных анимаций.
    • Реализован Компактный режим боковой панели (и опция автоматического сворачивания меню при узком экране), позволяющий освободить максимум пространства для работы с кодом и таблицами.