41.3. 分析器阶段

分析器阶段含两个部分:

41.3.1. 分析器

分析器必须检查(以纯 ASCII 文本方式到来的)查询字串的语法。 如果语法正确,则创建一个分析树并将之传回, 否则,返回一个错误。实现分析器和词法器我们使用了著名的 Unix 工具 lexyacc

词法器在文件 scan.l里定义,负责识别标识符SQL 关键字等。 对于发现的每个关键字或者标识符都会生成一个记号并且传递给分析器。

分析器在文件 gram.y 里定义并且包含一套语法规则和触发规则时执行的动作。 动作代码(实际上是 C 代码)用于建立分析树。

文件 scan.llex 转换成 C 源文件 scan.cgram.yyacc 转换成 gram.c。 在完成这些转换后,一个通用的 C 编译器就可以用于创建分析器。 千万不要对生成的 C 源文件做修改,因为下一次调用 lexyacc 时会把它们覆盖。

注意: 上面提到的转换和编译是使用跟随 PostgreSQL 发布的 makefiles 自动完成的。

yacc 或者 gram.y 里的语法规则的详细描述超出本文的范围。 有很多关于 lexyacc的书籍和文档。你在开始研究 gram.y 里给出的语法之前,你应该对 yacc 很熟悉,否则你是看不懂那里面的内容,理解不了发生了什么事情的。

41.3.2. 转换处理

分析器截断只使用和 SQL 语法结构相关的固定规则创建一个分析树。 它不会查找任何系统表,因此就不可能理解请求查询里面的详细的语意。 在分析器技术之后,转换处理接受分析器传过来的分析树然后做进一步处理, 解析那些理解查询中引用了哪个表,哪个函数以及哪个操作符的语意。所生成的表示这个信息的数据结构叫做查询树

把裸分析和语意分析分成两个过程的原因是系统表查找只能在一个事务中进行, 而我们不想在一接收到查询字串就发起一个事务。 裸分析阶段已经足够可以标识事务控制命令(BEGINROLLBACK,等), 并且这些东西不用任何进一步的分析就可以执行。一旦我们知道我们正在处理一个真正 的查询(比如SELECT 或者 UPDATE), 我们就可以发起一个事务了(如果我们还没开始这么一个)。只有这个时候可以调用转换处理。

转换处理生成的查询树结构上在很大程度上类似于裸分析树,但是在细节上有很多区别。 比如,在分析树里的 FuncCall 节点代表那些看上去像函数调用的东西。 根据引用的名字是一个普通函数还是一个聚集函数, 这个可能被转换成一个 FuncExpr 或者一个 Aggref 节点。 同样,有关字段和表达式结果的具体数据类型也添加到查询树中。