解析器阶段由两部分组成
解析器在 gram.y
和 scan.l
中定义,是使用 Unix 工具 bison 和 flex 构建的。
转换过程对解析器返回的数据结构进行修改和增强。
解析器必须检查查询字符串(以纯文本形式到达)的语法是否有效。如果语法正确,它会构建并反回一个解析树;否则它会返回错误。解析器和词法分析器是使用众所周知 Unix 工具 bison 和 flex 实现的。
词法分析器在文件 scan.l
中定义,负责识别标识符、SQL 关键字等。对于找到的每个关键字或标识符,都会生成一个标记并将其传递给解析器。
解析器在文件 gram.y
中定义,由一组语法规则和操作组成,每当触发规则时都会执行这些操作。操作的代码(实际上是 C 代码)用于构建解析树。
文件 scan.l
使用程序 flex 转换为 C 源文件 scan.c
,gram.y
使用 bison 转换为 gram.c
。在这些转换完成后,可以使用普通的 C 编译器创建解析器。切勿对生成的 C 文件进行任何更改,因为下次调用 flex 或 bison 时它们将被覆盖。
提到的转换和编译通常使用 PostgreSQL 源代码分发提供的Makefile 自动完成。
bison 或 gram.y
中给出的语法规则的详细描述超出了本手册的范围。有许多关于 flex 和 bison 的书籍和文档。在开始学习 gram.y
中给出的语法之前,您应该熟悉 bison,否则您将无法理解其中的内容。
解析器阶段使用仅针对 SQL 的语法结构的固定规则来创建解析树。它不会在系统目录中进行任何查找,因此无法理解请求操作的详细语义。在解析器完成后,转换过程将解析器返回的树作为输入,并执行理解查询引用的表、函数和运算符所需的语义解释。构建用于表示此信息的数据结构称为查询树。
将原始解析与语义分析分开的目的是,系统目录查找只能在事务中进行,我们不希望在接收到查询字符串后立即开始事务。原始解析阶段足以识别事务控制命令(BEGIN
、ROLLBACK
等),并且可以在没有任何进一步分析的情况下正确执行这些命令。一旦我们知道正在处理的是实际查询(例如 SELECT
或 UPDATE
),如果我们还没有在一个事务中,则可以开始一个事务。只有在此之后,才能调用转换过程。
转换过程创建的查询树在大多数地方在结构上类似原始解析树,但它在细节方面有很多差异。例如,解析树中的 FuncCall
节点表示看起来语法上像函数调用的东西。这可能会转换为 FuncExpr
或 Aggref
节点,具体取决于引用名称最终是普通函数还是聚合函数。此外,查询树中还添加了有关列和表达式结果的实际数据类型的的信息。