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