Раздел 10 · Databases

Своя СУБД на Haskell · урок 6: анализ, план, исполнитель, EXPLAIN

senior~120 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Своя СУБД на Haskell · урок 6: анализ, план, исполнитель, EXPLAIN

Кому это. Ты прошёл урок 5: строка SQL превращается в дерево. Под ним хранилище из уроков 1 до 4: страницы, пул, куча и индекс. Сегодня их соединит верхняя половина движка, и SELECT * FROM users WHERE id = 2 наконец вернёт строку. Тот же урок есть в TS-треке. В Haskell-порте видна граница, к которой мы шли пять уроков: binder и планировщик это чистые функции над видом каталога, Either DbError от начала до конца, а IO начинается только там, где операторы тянут строки из кучи через Stream из урока 3.

Важно про код. Урок большой: это вся верхняя половина движка разом. Но ни одна фаза не возится с байтами. Эталон ourdb на Haskell написан сразу под все одиннадцать уроков, и его операторы уже несут контекст транзакции, блокировки и журнал. В листингах этого урока показана форма этапа 6, та самая, что проверяют тесты этапа: строки про журнал и транзакции (уроки 7 до 9) вырезаны, и каждое такое место отмечено комментарием -- урок N:. Сигнатуры от этого становятся короче, но имена и устройство функций те же, что в эталоне.

Где мы в движке

┌────────────────────────────────────────────────┐
│  Бинарный протокол + TCP-сервер                  │  урок 11
├────────────────────────────────────────────────┤
│  Исполнитель (Volcano) + EXPLAIN                 │  урок 6  ← мы здесь
│  Парсер, лексер, pretty-printer                  │  урок 5
├────────────────────────────────────────────────┤
│  Vacuum                                          │  урок 10
│  Блокировки (2PL), граф ожидания                 │  урок 9
│  MVCC, менеджер транзакций                       │  урок 8
│  WAL (журнал, redo, recovery)                    │  урок 7
├────────────────────────────────────────────────┤
│  B+Tree индекс                                   │  урок 4
│  Куча (slotted pages, seq scan)                  │  урок 3
├────────────────────────────────────────────────┤
│  Буферный пул (кэш страниц, вытеснение)           │  урок 2
│  DiskManager (страницы поверх файла)              │  урок 1
└────────────────────────────────────────────────┘

Конвейер запроса

Text ──parseCommand──▶ Command ──bind──▶ LogicalPlan ──plan──▶ Operator ──pull──▶ строки
        (урок 5, чисто)          (чисто, вид каталога)   (чисто, вид каталога)   (IO, Stream)

Четыре фазы, и только последняя живёт в IO.

  • Разбор. parseCommand из урока 5 даёт Command.
  • Анализ (binding). Binder берёт дерево и вид каталога, проверяет, что таблица и колонки существуют, превращает имена колонок в их номера и выводит типы выражений. Результат это LogicalPlan: что запрос хочет сделать, без решения как.
  • Планирование. Планировщик решает как: пройти всю кучу или спуститься по индексу. Результат это дерево физических операторов.
  • Исполнение. Операторы тянут строки друг из друга по одной, это модель Volcano.

Binder и планировщик не трогают диск вообще. Им нужен только вид каталога: функция, которая по имени таблицы отдаёт схему или доступ к куче. Каталог отдаёт такой вид в начале каждого запроса, и дальше обе фазы это Either DbError без единого IO.

Ошибки движка: тот же DbError

Новых типов ошибок нет. Binder возвращает EngineError с текстом (“неизвестная колонка “bad""), проверка уникальности первичного ключа бросает UniquenessError. Оба конструктора уже есть в DbError с урока 1. В TS-эталоне UniquenessError это подкласс EngineError, и тесты проверяют instanceof EngineError. В Haskell подклассов нет, поэтому в OurDb.Error есть предикат:

-- из src/OurDb/Error.hs
-- | EngineError или его подкласс (в эталоне UniquenessError и прочие наследуют EngineError).
isEngineError :: DbError -> Bool
isEngineError = \case
  SqlError {} -> False
  StorageError {} -> False
  _ -> True

engineError :: Text -> IO a
engineError = throwIO . EngineError

дальше после входа

Урок целиком открыт всем, кто вошёл. Войди по своему email, чтобы читать дальше.

Вход пока по приглашению. Если хочешь учиться, напиши Vassiliy.