49.3. Этап разбора#
49.3. Этап разбора #
Этап разбора состоит из двух частей:
Парсер parser, определенный в
gram.y
иscan.l
. построен с помощью Unix-инструментов bison и flex.Процесс трансформации вносит изменения и дополнения в структуры данных, возвращаемые парсером.
49.3.1. Парсер #
Парсер должен проверить строку запроса (которая поступает в виде обычного текста) на наличие корректного синтаксиса. Если синтаксис правильный, строится и возвращается дерево разбора; в противном случае возвращается ошибка. Парсер и лексер реализованы с использованием известных инструментов Unix bison и flex.
Лексер определен в файле scan.l
и отвечает за распознавание идентификаторов, ключевых слов SQL и т.д. Для каждого найденного ключевого слова или идентификатора генерируется токен и передается парсеру.
Парсер определен в файле gram.y
и состоит из набора правил грамматики и действий, которые выполняются каждый раз, когда срабатывает правило. Код действий (который на самом деле является кодом на языке C) используется для построения дерева разбора.
Файл scan.l
преобразуется в исходный файл на языке C scan.c
с помощью программы flex, а файл gram.y
преобразуется в gram.c
с использованием bison. После выполнения этих преобразований можно использовать обычный компилятор C для создания парсера. Никогда не вносите изменения в сгенерированные файлы на языке C, так как они будут перезаписаны при следующем вызове flex или bison.
Примечание
Упомянутые преобразования и компиляции обычно выполняются автоматически с использованием makefiles, поставляемых в дистрибутиве исходного кода Tantor BE.
Подробное описание bison или
грамматические правила, указанные в gram.y
, выходят за рамки данного руководства. Существует множество книг и документов, посвященных flex и bison. Необходимо ознакомиться с bison, прежде чем начать изучать грамматику, указанную в gram.y
, иначе вы не поймете, что там происходит.
49.3.2. Процесс преобразования #
Этап разбора создает дерево разбора, используя только фиксированные правила о синтаксической структуре SQL. Он не выполняет поиск в системных каталогах, поэтому нет возможности понять детальную семантику запрашиваемых операций. После завершения разбора процесс трансформации берет дерево, возвращенное парсером, в качестве входных данных и выполняет семантическую интерпретацию, необходимую для понимания, на какие таблицы, функции и операторы ссылается запрос. Структура данных, которая строится для представления этой информации, называется деревом запроса.
Причина разделения сырого разбора от семантического анализа заключается в том, что поиск в системном каталоге может быть выполнен только в рамках транзакции, и мы не хотим начинать транзакцию сразу после получения строки запроса. Стадия сырого разбора достаточна для идентификации команд управления транзакцией (BEGIN
, ROLLBACK
и т. д.), и эти команды могут быть правильно выполнены без дополнительного анализа. Когда мы узнаем, что имеем дело с фактическим запросом (например, SELECT
или UPDATE
), можно начать транзакцию, если мы еще не находимся в ней. Только после этого может быть вызван процесс преобразования.
Дерево запроса, созданное процессом преобразования, структурно похоже на исходное дерево разбора в большинстве случаев, но имеет много отличий в деталях. Например, узел FuncCall
в дереве разбора представляет что-то, что выглядит синтаксически как вызов функции. Это может быть преобразовано в узел FuncExpr
или Aggref
в зависимости от того, является ли ссылочное имя обычной функцией или агрегатной функцией. Также в дерево запроса добавляется информация о фактических типах данных столбцов и результатов выражений.