openGauss源码解析(89)

openGauss源码解析:SQL引擎源解析(4)

6.2.2 语法分析

openGuass中定义了bison工具能够识别的语法文件gram.y,同样在Makefile中可以通过bison工具对gram.y进行编译,生成gram.cpp文件。

在openGauss中,根据SQL语言的不同定义了一系列表达Statement的结构体(这些结构体通常以Stmt作为命名后缀),用来保存语法分析结果。以SELECT查询为例,它对应的Statement结构体如下。

typedef struct SelectStmt {

NodeTag type; // 节点类型

List* distinctClause; // DISTINCT子句

IntoClause* intoClause; // SELECT INTO子句

List* targetList; // 目标属性

List* fromClause; // FROM子句

Node* whereClause; // WHERE子句

List* groupClause; // GROUP BY子句

Node* havingClause; // HAVING子句

List* windowClause; // WINDOW子句

WithClause* withClause; // WITH子句

List* valuesLists; // FROM子句中未转换的表达式,用来保存常量表

List* sortClause; // ORDER BY子句

Node* limitOffset; // OFFSET子句

Node* limitCount; // LIMIT子句

List* lockingClause; // FOR UPDATE子句

HintState* hintState;

SetOperation op; // 查询语句的集合操作

bool all; // 集合操作是否指定ALL关键字

struct SelectStmt* larg; // 左子节点

struct SelectStmt* rarg; // 右子节点

……

} SelectStmt;

这个结构体可以看作一个多叉树,每个叶子节点都表达了SELECT查询语句中的一个语法结构,对应到gram.y中,它会有一个SelectStmt。代码如下:

simple_select:

SELECT hint_string opt_distinct target_list

into_clause from_clause where_clause

group_clause having_clause window_clause

{

SelectStmt *n = makeNode(SelectStmt);

n->distinctClause = $3;

n->targetList = $4;

n->intoClause = $5;

n->fromClause = $6;

n->whereClause = $7;

n->groupClause = $8;

n->havingClause = $9;

n->windowClause = $10;

n->hintState = create_hintstate($2);

n->hasPlus = getOperatorPlusFlag();

$$ = (Node *)n;

}

……

simple_select除了上面的基本形式,还可以表示为其他形式,如VALUES子句、关系表达式、多个SELECT语句的集合操作等,这些形式会进一步的递归处理,最终转换为基本的simple_select形式。代码如下:

simple_select:

……

| values_clause { $$ = $1; }

| TABLE relation_expr

……

| select_clause UNION opt_all select_clause

{

$$ = makeSetOp(SETOP_UNION, $3, $1, $4);

}

| select_clause INTERSECT opt_all select_clause

{

$$ = makeSetOp(SETOP_INTERSECT, $3, $1, $4);

}

| select_clause EXCEPT opt_all select_clause

{

$$ = makeSetOp(SETOP_EXCEPT, $3, $1, $4);

}

| select_clause MINUS_P opt_all select_clause

{

$$ = makeSetOp(SETOP_EXCEPT, $3, $1, $4);

}

;

从simple_select语法分析结构可以看出,一条简单的查询语句由以下子句组成:去除行重复的distinctClause、目标属性targetList、SELECT INTO子句intoClause、FROM子句fromClause、WHERE子句whereClause、GROUP BY子句groupClause、HAVING子句havingClause、窗口子句windowClause和plan_hint子句。在成功匹配simple_select语法结构后,将会创建一个Statement结构体,将各个子句进行相应的赋值。对simple_select而言,目标属性、FROM子句、WHERE子句是最重要的组成部分。

目标属性对应语法定义中的target_list,由若干个target_el组成。target_el可以定义为表达式、取别名的表达式和“*”等。代码如下:

target_list:

target_el { $$ = list_make1($1); }

| target_list ',' target_el { $$ = lappend($1, $3); }

;

target_el: a_expr AS ColLabel

……

| a_expr IDENT

……

| a_expr

……

| '*'

……

| c_expr VALUE_P

……

| c_expr NAME_P

……

| c_expr TYPE_P

……

;

当成功匹配到一个target_el后,会创建一个ResTarget结构体,用于存储目标对象的全部信息。ResTarget结构如下。

typedef struct ResTarget {

NodeTag type;

char *name; // AS指定的目标属性的名称,没有则为空

List *indirection; // 通过属性名、*号引用的目标属性,没有则为空

Node *val; // 指向各种表达式

int location; // 符号出现的位置

} ResTarget;

FROM子句对应语法定义中的from_clause,由FROM关键字和from_list组成,而from_list则由若干个table_ref组成。table_ref可以定义为关系表达式、取别名的关系表达式、函数、SELECT语句、表连接等形式。代码如下:

from_clause:

FROM from_list { $$ = $2; }

| /*EMPTY*/{ $$ = NIL; }

;

from_list:

table_ref { $$ = list_make1($1); }

| from_list ',' table_ref { $$ = lappend($1, $3); }

;

table_ref: relation_expr

……

| relation_expr alias_clause

……

| relation_expr opt_alias_clause tablesample_clause

……

| relation_expr PARTITION '(' name ')'

……

| relation_expr BUCKETS '(' bucket_list ')'

……

| relation_expr PARTITION_FOR '(' maxValueList ')'

……

| relation_expr PARTITION '(' name ')' alias_clause

……

| relation_expr PARTITION_FOR '(' maxValueList ')'

alias_clause

……

| func_table

……

| func_table alias_clause

……

| func_table AS '(' TableFuncElementList ')'

……

| func_table AS ColId '(' TableFuncElementList ')'

……

| func_table ColId '(' TableFuncElementList ')'

……

| select_with_parens

……

| select_with_parens alias_clause

……

| joined_table

……

| '(' joined_table ')' alias_clause

……

;

以FROM子句中的关系表达式为例,最终会定义为ColId的相关形式,表示为表名、列名等的定义。代码如下:

relation_expr:

qualified_name

……

| qualified_name '*'

……

| ONLY qualified_name

……

| ONLY '(' qualified_name ')'

……

;

qualified_name:

ColId

……

| ColId indirection

……

在捕获到ColId后,会创建一个RangeVar结构体,用来存储相关信息。RangeVar结构如下。

typedef struct RangeVar {

NodeTag type;

char* catalogname; // 表的数据库名

char* schemaname; // 表的模式名

char* relname; // 表或者序列名

char* partitionname; //记录分区表名

InhOption inhOpt; // 是否将表的操作递归到子表上

char relpersistence; / 表类型,普通表/unlogged表/临时表/全局临时表

Alias* alias; // 表的别名

int location; // 符号出现的位置

bool ispartition; // 是否为分区表

List* partitionKeyValuesList;

bool isbucket; // 当前是否为哈希桶类型的表

List* buckets; // 对应的哈希桶中的桶

int length;

#ifdef ENABLE_MOT

Oid foreignOid;

#endif

} RangeVar;

WHERE子句给出了元组的约束信息,对应语法定义中的where_clause,由WHERE关键字和一个表达式组成。例如:

where_clause:

WHERE a_expr { $$ = $2; }

| /*EMPTY*/ { $$ = NULL; }

;

表达式可以为一个常量表达式或者属性,也可以为子表达式的运算关系。例如:

a_expr: c_expr { $$ = $1; }

| a_expr TYPECAST Typename

{ $$ = makeTypeCast($1, $3, @2); }

| a_expr COLLATE any_name

……

| a_expr AT TIME ZONE a_expr

……

| '+' a_expr

……

;

对于运算关系,会调用makeSimpleA_Expr函数生成A_Expr结构体,存储表达式的相关信息。A_Expr结构如下,字段lexpr和rexpr分别保存左、右两个子表达式的相关信息。代码如下:

typedef struct A_Expr {

NodeTag type;

A_Expr_Kind kind; // 表达式类型

List *name; // 操作符名称

Node *lexpr; // 左子表达式

Node *rexpr; // 右子表达式

int location; // 符号出现的位置

} A_Expr;

simple_select的其他子句,如distinctClause、groupClause、havingClause等,语法分析方式类似。而其他SQL命令,如CREATE、INSERT、UPDATE、DELETE等,处理方式与SELECT命令类似,这里不做一一说明。

对于任何复杂的SQL语句,都可以拆解为多个基本的SQL命令执行。在完成词法分析和语法分析后,raw_parser函数会将所有的语法分析树封装为一个List结构,名为raw_parse_tree_list,返回给exec_simple_query函数,用于后面的语义分析、查询重写等步骤,该List中的每个ListCell包含一个语法树。

posted @ 2024-04-30 10:10  openGauss-bot  阅读(11)  评论(0编辑  收藏  举报