转换处理对分析器返回的数据结构进行修改和增补。
lexer (词法)在文件 scan.l 里定义,负责识别标识符,SQL 关键字等。对于发现的每个关键字或者标识符都会生成一个记号并且传递给分析器。
分析器在文件 gram.y 里定义并且包含一套语法规则和触发规则时执行的动作。动作代码(实际上是 C 代码)用语建立分析树。
文件 scan.l 用 lex 转换成 C 源文件而 gram.y 用 yacc 转换成 gram.c。在完成这些转换后,一个通用的 C 编译器可以用于创建分析器。千万不要对生成的 C 源文件做修改,因为下一次调用 lex 或 yacc 会把它们覆盖。
注意:上面提到的转换和编译是使用跟随Postgres 发布的 makefiles 自动完成的。
为了更好地理解处理一个查询时 Postgres 里使用的数据结构,我们用一个简单的例子演示在每个阶段数据结构所做的改变。
例 54-1. 一个简单的选择(Select)
这个例子包含下面的简单的查询,这个例子将会在本章剩余各节的所有描述和图示里出现。该查询假设在Supplier Database 数据库里的表已经定义了。
select s.sname, se.pno from supplier s, sells se where s.sno > 2 and s.sno = se.sno;
树的顶端节点是 SelectStmt 节点。对每个在 SQL 查询的 from子句里出现的元素创建一个 RangeVar 节点,存放 alias(别名)的名称和一个指向一个存放关系名称的RelExpr 节点的指针。所有 RangeVar 节点都收集到一个列表里,然后附加到 SelectStmt 节点的 fromClause 字段上。
对于 SQL 查询里面的 select列表 里出现的每个元素都创建一个 ResTarget 节点,存放一个指向 Attr 节点的指针。Attr 节点存放元素的关系名称和一个指向存放着字段名称的 Value 节点的指针。所有 ResTarget 节点都收集到一个列表里,然后链接到 SelectStmt 节点的 targetList 字段里。
图 \ref{where_clause} 显示了为例子 select s.sname, se.pno from supplier s, sells se where s.sno > 2 and s.sno = se.sno; 里 SQL 查询的 where 子句构建的操作符树,这个操作符树附加到了 SelectStmt 节点的字段 qual 上。操作符树的顶端节点是一个代表 AND 操作的 A_Expr 节点。这个节点有两个后继节点, lexpr 和 rexpr 分别指向两个子树。附加到 lexpr 的子树代表条件 s.sno > 2 而附加到 rexpr 的子树代表 s.sno = se.sno。为每个字段创建一个 Attr 节点,存放关系名和一个指向存放着字段名的 Value 节点的指针。为在查询里出现的常量条款创建一个 Const 节点,存放常量值。
现在进行一个检查,看看 FROM 子句里面的关系名是否被系统所知。为每个系统表里面出现的关系名创建一个RTE 节点,该节点包含关系名,别名和关系 id。从现在开始,关系 id (标识)用于指代查询里出现的关系。所有RTE 节点都收集到范围表项目列表(range table entry list)里,然后该列表在链接到 Query 节点的 rtable 字段上。如果查询里面有一个不为系统所知的关系被检测到,那么将返回一个错误然后查询处理将退出。
下一步是检查所用的字段名是否包含在查询给出的关系里。为找到的每个字段创建一个TLE 节点,保存一个指向 Resdom 节点的(该节点里保存列名称)的指针和一个指向 VAR 节点的指针。在 VAR 节点里有两个重要的数字。数据域 varno 包含当前字段的关系在上面创建的范围表项目列表里面的位置。数据域 varattno 给出该字段在关系里的位置。如果无法找到一个字段的名称,则返回一个错误而且这个正在处理的查询将退出。