openGauss源码解析(91)
openGauss源码解析:SQL引擎源解析(6)
6.2.4 解析流程分析
在了解了SQL解析的大致流程后,通过一个具体的案例了解一下SQL解析过程中的具体代码流程。首先创建基表warehouse,语句如下。
CREATE TABLE warehouse
(
w_id SMALLINT PRIMARY KEY,
w_name VARCHAR(10) NOT NULL,
w_street_1 VARCHAR(20) CHECK(LENGTH(w_street_1)<>0),
w_street_2 VARCHAR(20) CHECK(LENGTH(w_street_2)<>0),
w_city VARCHAR(20),
w_state CHAR(2) DEFAULT 'CN',
w_zip CHAR(9),
w_tax DECIMAL(4,2),
w_ytd DECIMAL(12,2)
);
warehouse表被创建之后,会在pg_class系统表中生成一条元数据,元数据中的OID属性用来用来代表这个表,比如在pg_attribute表中就通过这个OID来标明这些属性是属于哪个表的。假设warehouse的OID为16000,下面以查询语句SELECT w_name FROM warehouse WHERE w_no = 1为例,来分析SQL分析的整体流程。
如表6-5所示,scan.l会划分SQL语句中的各个token及其词性,利用关键字列表匹配到关键字SELECT、FROM、WHERE,并将其他单词w_name、warehouse、w_no标记为标识符,将符号“=”识别为操作符,“1”识别为整数型常量。
表6-5 token及其词性
词性 | 内容 | Scan.l中的划分 |
关键字 | SELECT、FROM、WHERE | SELECT/FROM/WHERE |
标识符 | w_name、warehouse、w_no | IDENT |
操作符 | = | = |
常量 | 1 | ICONST |
在完成SQL语句的词法分析后,scan.l生成词法分析结果,代码如下:
SELECT IDENT FROM IDENT WHERE IDENT “=” ICONST
gram.y文件会利用语法规则进行解析,生成语法树。如图6-3所示,对于本节给出的SQL语句,openGauss会匹配SelectStmt下的simple_select语法生成语法树,进而根据目标属性、FROM子句和WHERE子句创建ResTarget、RangeVar、A_Expr三个结构体,这三个结构体分别存储在语法树的target_list、from_clause、where_clause字段下,如果没有其他子句,对应字段为空。
图6-3 gram.y文件解析流程
图6-4给出了语法树的内存组织结构。一个查询语法树SelectStmt的目标属性是包含若干ResTarget的targetList链表、fromClause和whereClause。
(1) targetList链表中ResTarget字段val会根据目标属性的类型,指向不同的结构体。对于本节给出的用例,val指向结构体ColumnRef,存储目标属性在源表中的具体信息。
(2) fromClause存储FROM子句的指向对象,同样是包含若干个RangeVar结构体的链表,每个RangeVar存储范围表的具体信息。对于本节给出的用例,只有一个RangeVar结构体,字段relname值为warehouse。
(3) whereClause为Node结构,存储WHERE子句包含的范围表达式,根据表达式的不同,使用不同的结构体存储,如列引用ColumnRef、参数引用ParamRef、前缀/中缀/后缀表达式A_Expr、常量A_Const。对于本节给出的用例,使用A_Expr来存储表达式对象,并分别使用ColumnRef和A_Const存储左、右两个子表达式的具体信息。
图6-4 语法树内存组织结构图
在完成词法分析和语法分析后,parse_analyze函数会根据语法树的类型,调用transformSelectStmt将parseTree改写为查询树。在改写过程中,parse_analyze除了会检查SQL命令是否符合语义规定,还会根据语法树对象获得更有利于执行的信息,比如表的OID、列的编号等。对于本节给出的用例,查询树对应的内存组织结构如图6-5所示,目标属性、FROM子句和WHERE子句的语义分析结果会分别保存在结构体TargetEntry、RangeTblEntry、FromExpr中。
图6-5 查询树内存组织结构图
完成语义分析后,SQL解析过程完成,SQL引擎开始执行查询优化。