Learning Platform
Глоссарий Troubleshooting
Урок 05.01 · 21 мин
Средний
query-lifecycleparserASTstatement

От SQL-текста к запросу: statement, parser, AST

Вы отправляете в Trino строку: SELECT name FROM customer WHERE acctbal > 1000. Через секунды приходит результат. Между этими двумя моментами движок проделывает длинную цепочку преобразований — это жизненный цикл запроса, позвоночник всего курса. Модуль 04 проходит цикл от начала до конца. Этот, первый, урок — про самый первый шаг: превращение текста SQL в структуру, с которой движок может работать.

Текст SQL — это просто последовательность символов. Программа не может «понять» строку напрямую; ей нужна структура. Разберём, как Trino эту структуру строит и зачем нужны понятия statement, query, parser и AST.


Statement и query: текст против выполнения

Два слова, которые легко спутать, но Trino их строго различает.

Statement — это сам текст SQL, переданный в Trino. Просто строка символов. SELECT name FROM customer — это statement.

Query — это runtime-инстанс, который Trino создаёт, обработав statement. Query — живой объект: у него есть идентификатор, состояние, он объединяет всё, что породит выполнение, — stages, tasks, splits, обращения к коннекторам. Statement — это «что попросили», query — это «выполнение этого по жизни».

Statement против query
StatementТекст SQL как строка символов. Пассивный ввод. Может быть синтаксически неверным.
Trino обрабатывает
QueryRuntime-инстанс: объект с идентификатором и состоянием, объединяющий stages, tasks, splits.

Различие не педантизм. Один statement, выполненный дважды, породит два разных query с разными идентификаторами и своими наборами задач. Когда вы смотрите в Web UI «список запросов» — вы смотрите список query (runtime-инстансов), а не текстов. Текст внутри них может совпадать, query — всегда разные.

Превращение statement в query — не один шаг, а конвейер. Самое первое звено — синтаксический разбор, парсинг.


Зачем нужен parser

Текст SELECT name FROM customer WHERE acctbal > 1000 для движка — это поток байтов. Чтобы что-то с ним сделать, движку нужно ответить на вопросы: где здесь список выбираемых столбцов, где имя таблицы, где условие фильтра, какие части условия — операнды, а какая — оператор. Плоская строка на эти вопросы не отвечает.

Parser (синтаксический анализатор) — компонент, который читает текст SQL и строит из него дерево. Парсинг идёт в два логических этапа:

  1. Лексический анализ. Поток символов разбивается на токены — неделимые лексические единицы: ключевое слово SELECT, идентификатор name, ключевое слово FROM, идентификатор customer, оператор >, числовой литерал 1000. Пробелы и переводы строк отбрасываются. Из строки получается список токенов.

  2. Синтаксический анализ. Список токенов сопоставляется с грамматикой SQL — формальным набором правил, описывающим, какие последовательности токенов допустимы. Грамматика говорит: SELECT сопровождается списком выражений, затем может идти FROM с источником, затем опционально WHERE с булевым условием. По грамматике из плоского списка токенов строится дерево.

Trino описывает грамматику SQL формально и генерирует по ней код парсера инструментом ANTLR. Это значит, что грамматика — не код, написанный руками, а декларативная спецификация языка, из которой парсер получается автоматически. Практический итог: Trino принимает чётко определённый диалект SQL, и любое отклонение от грамматики ловится здесь, на парсинге.

NOTE

Парсер проверяет только синтаксис — форму. Он убедится, что SELECT построен по правилам грамматики. Но парсер НЕ знает, существует ли таблица customer и есть ли в ней столбец name. Проверка существования имён и совместимости типов — это уже семантический анализ, отдельный следующий этап. Парсер работает с формой, а не со смыслом.


AST: дерево синтаксиса

Результат работы парсера — AST, Abstract Syntax Tree (абстрактное синтаксическое дерево). Это и есть та структура, которой так не хватало в плоской строке. AST представляет SQL-запрос как дерево вложенных узлов, где каждый узел — синтаксическая конструкция, а связи родитель-потомок отражают вложенность.

Возьмём запрос и посмотрим его AST концептуально:

SELECT name
FROM customer
WHERE acctbal > 1000
AST запроса SELECT как дерево
QueryКорень дерева — узел всего запроса. Объединяет SELECT, FROM, WHERE.
SelectУзел списка выбора. Внутри — выражение-столбец name.
FromУзел источника данных. Внутри — ссылка на таблицу customer.
WhereУзел условия фильтра. Внутри — булево выражение-сравнение.

Узел Where сам по себе не лист — внутри него вложено дерево булевого выражения acctbal > 1000:

Поддерево выражения acctbal больше 1000
Comparison >Узел операции сравнения 'больше'. У него два потомка — левый и правый операнды.
IdentifierЛевый операнд: ссылка на столбец acctbal.
LiteralПравый операнд: числовой литерал 1000.

Дерево, а не строка, потому что SQL рекурсивен по природе. Выражение содержит подвыражения, подзапрос содержит полный запрос, условие может вкладывать условия. Только древовидная структура естественно выражает эту вложенность. Слово «abstract» в названии означает, что дерево хранит смысловую структуру, а не точное написание: пробелы, переводы строк, необязательные скобки в AST не попадают — они нужны были только для разбора.

С AST уже можно работать программно. Движок обходит дерево, узнаёт типы узлов, добирается до любой части запроса по ветке. Все дальнейшие этапы цикла — анализ, планирование — оперируют не текстом, а этим деревом и его потомками.


Что происходит при синтаксической ошибке

Поскольку парсер сверяет токены с грамматикой, любая конструкция, не укладывающаяся в грамматику, отлавливается прямо здесь — раньше всех остальных этапов. Это синтаксическая ошибка.

-- Пропущено имя таблицы после FROM
SELECT name FROM WHERE acctbal > 1000;
Query failed: line 1:18: mismatched input 'WHERE'.
Expecting: '(', 'JSON_TABLE', 'LATERAL', 'TABLE', 'UNNEST', <identifier>

Сообщение показывает позицию (line 1:18), что парсер встретил (WHERE) и что грамматика разрешала встретить в этом месте. После FROM грамматика ждёт источник данных — идентификатор таблицы, подзапрос в скобках, UNNEST и так далее — а получила ключевое слово WHERE. Несоответствие грамматике, разбор останавливается.

WARNING

Синтаксическая ошибка ловится на парсинге — это самый ранний этап. До планирования и тем более до распределённого исполнения дело не доходит вообще: запрос отклоняется ещё до того, как стать полноценным query. Поэтому синтаксические ошибки дешёвые — кластер не тратит на них ресурсы воркеров.

Важно, чего на этом этапе ещё НЕ проверяется. Запрос SELECT nonexistent_column FROM nonexistent_table синтаксически безупречен — он построен по грамматике. Парсер построит для него корректный AST. Что таблицы не существует, выяснится только на следующем этапе — семантическом анализе. Граница чёткая: парсер отвечает за форму, семантический анализ — за смысл.


Место парсинга в жизненном цикле

Зафиксируем картину целиком, чтобы видеть, куда мы движемся дальше в модуле.

Если разложить жизненный цикл в линию: statement (текст SQL) проходит парсинг и становится AST; AST проходит семантический анализ и становится Analysis; дальше идёт логическое планирование, оптимизация, распределённое планирование и исполнение. Парсинг — самое первое звено этой цепи.

Парсинг — это переход «текст -> структура». Без него никакой дальнейший анализ невозможен: нельзя проверять типы или строить план для плоской строки символов. AST — первая структура в длинной цепочке, и каждый следующий этап будет преобразовывать представление запроса дальше: AST станет аналитической моделью, та — логическим планом, план — распределённым планом. Жизненный цикл запроса это и есть последовательность таких преобразований представления.


Попробуй сам

На кластере Trino поэкспериментируйте с границей между синтаксисом и смыслом:

  1. Выполните заведомо синтаксически неверный запрос, например SELECT FROM customer или SELECT name customer FROM. Прочитайте сообщение: где позиция ошибки, что парсер ожидал.
  2. Выполните синтаксически верный, но бессмысленный запрос: SELECT * FROM tpch.sf1.no_such_table. Сравните сообщение об ошибке с предыдущим. Это уже не синтаксическая ошибка — таблица не найдена на этапе анализа.
  3. Выполните SELECT col_that_does_not_exist FROM tpch.sf1.nation. Снова: запрос построен по грамматике, AST для него существует, но столбца нет.
  4. Сформулируйте письменно: за какую категорию ошибок отвечает парсер, а какие ошибки в принципе не могут быть пойманы на парсинге и почему.

Это разделение «форма против смысла» — ключ к пониманию следующего урока про семантический анализ.


Проверка знанийKnowledge check
Что такое AST, почему запрос представляется именно деревом, и какую категорию ошибок ловит парсер, а какую — нет?
ОтветAnswer
AST (Abstract Syntax Tree) — это абстрактное синтаксическое дерево, структурное представление SQL-запроса, которое строит парсер из текста. Парсер работает в два этапа: лексический анализ разбивает текст на токены (ключевые слова, идентификаторы, операторы, литералы), а синтаксический анализ сопоставляет токены с формальной грамматикой SQL и по ней строит дерево. Запрос представляется именно деревом, потому что SQL рекурсивен по природе: выражение содержит подвыражения, подзапрос содержит полный запрос, условие вкладывает условия — только древовидная структура естественно выражает эту вложенность через связи родитель-потомок. Слово abstract означает, что дерево хранит смысловую структуру, а не точное написание: пробелы, переводы строк, лишние скобки в AST не попадают. Парсер ловит только синтаксические ошибки — нарушения грамматики, формы запроса. Он НЕ проверяет смысл: существует ли таблица, есть ли в ней нужный столбец, совместимы ли типы — это уже задача следующего этапа, семантического анализа. Парсер отвечает за форму, не за смысл.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. В чём разница между statement и query в терминологии Trino?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 6