1 概述
simple-select是一条SELECT语句的最核心部分,从simple-select的语法定义可以看出,它由如下子句组成:去除行重复的DISTINCT (标识符opt-distinct)、目标属性(标识符target-list)、 SELECT INTO子句(标识符into-clause)、FROM子句(标识符from-clause)、 WHERE子句(标识符where_clause) 、 CROUP BY子句(标识符group-clause)、HAVING子句(标识符having-clause)和窗口子句(标识符window-clause)。在成功匹配simple-select语法结构后,将创建一个SelectStmt结构体,并将各子句赋值到结构体中相应的字段。
此外, simple-select还可以定义为其他的形式(见文件Gram. y),如VALUES子句、关系表达式以及多个SELECT语句的交并差等,但这些情况最终都会转化成最基本的simple_select形式来处理。而对于simple-select来说,目标属性(标识符target list)、FROM子句(标识符from-clause)、 WHERE子句(标识符where-clause)以及GROUP BY子句(标识符groupclause)是最重要的部分,下面将结合一个具体实例对这些子句的处理加以详细介绍。
simple_select:
SELECT opt_distinct target_list
into_clause from_clause where_clause
group_clause having_clause window_clause
{
SelectStmt *n = makeNode(SelectStmt);
n->targetList = $3;
n->intoClause = $4;
n->fromClause = $5;
n->whereClause = $6;
n->groupClause = $7;
n->havingClause = $8;
n->windowClause = $9;
$$ = (Node *)n;
}
| SELECT distinct_clause target_list
into_clause from_clause where_clause
group_clause having_clause window_clause
{
SelectStmt *n = makeNode(SelectStmt);
n->distinctClause = $2;
n->targetList = $3;
n->intoClause = $4;
n->fromClause = $5;
n->whereClause = $6;
n->groupClause = $7;
n->havingClause = $8;
n->windowClause = $9;
$$ = (Node *)n;
}
| values_clause { $$ = $1; }
2 子句
2.1 DISTINCT子句
DISTINCT子句对应语法定义中的标识符opt_distinct。从opt-distinct的语法结构可以看到,它可以匹配DISTINCT, ALL, DISTINCT ON (表达式列表)或者为空,用来决定SELECT语句是否去除重复的行。
当匹配到DISTINCT时, opt_distinct返回一个List,该链表的第一个ListCell的ptr-value字段置为空。
当匹配到DISTINCT ON时, opt_distinct也返回一个List,这个List中包含了跟在DISTINCTON之后的表达式的列表(星号或者表的属性等)。
当匹配到ALL或者空时, opt_distinct返回NIL,表明没有使用DISTINCT。
2.2 目标属性
目标属性是SELECT语句中所要查询的属性列表,对应着语法定义中的标识符targetList, targetList由若干个target_el组成,target_el定义为取别名的表达式、表达式以及“*”等。
target_list:
target_el { $$ = list_make1($1); }
| target_list ',' target_el { $$ = lappend($1, $3); }
;
target_el: a_expr AS ColLabel
{
$$ = makeNode(ResTarget);
$$->name = $3;
$$->indirection = NIL;
$$->val = (Node *)$1;
$$->location = @1;
}
/*
* We support omitting AS only for column labels that aren't
* any known keyword. There is an ambiguity against postfix
* operators: is "a ! b" an infix expression, or a postfix
* expression and a column label? We prefer to resolve this
* as an infix expression, which we accomplish by assigning
* IDENT a precedence higher than POSTFIXOP.
*/
| a_expr IDENT
{
$$ = makeNode(ResTarget);
$$->name = $2;
$$->indirection = NIL;
$$->val = (Node *)$1;
$$->location = @1;
}
| a_expr
{
$$ = makeNode(ResTarget);
$$->name = NULL;
$$->indirection = NIL;
$$->val = (Node *)$1;
$$->location = @1;
}
| '*'
{
ColumnRef *n = makeNode(ColumnRef);
n->fields = list_make1(makeNode(A_Star));
n->location = @1;
$$ = makeNode(ResTarget);
$$->name = NULL;
$$->indirection = NIL;
$$->val = (Node *)n;
$$->location = @1;
}
;
当成功匹配一个targetList时,创建一个ResTarget结构体,该结构体中存储了该属性的全部信息。最终targetList将返回一个由ResTarget构成的List。
targetList包括若干ListCell节点,每个节点中的data字段指向结构体ResTarget,用来表示目标属性中的一项。下图展示了目标属性在内存中的组织结构。当目标属性中的某项涉及函数调用时, ResTarget中的字段val会指向结构体FunCall, FunCall的字段funcname存储函数的名称,字段args指向结构体ColumnRef构成的链表,每一个ColumnRef存储了函数调用中所使用到的表的一个属性。如果没有函数调用,则结构体ResTarget中的字段val直接指向结构体ColumnRef,存储该项目标属性所涉及的表的字段(此种情况没有在图中展示)。
2.3 FROM子句
文件gram. y中定义的标识符from_clause表示SELECT语句中的FROM子句, from_clause由FROM关键字和fromList组成。而fromlist则由若干个标识符table_ref组成,每一个table_ref表示FROM子句中用逗号分隔的每个子项,它表示在FROM子句中出现的一个表或者一个子查询。
from_clause:
FROM from_list { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }
;
from_list:
table_ref { $$ = list_make1($1); }
| from_list ',' table_ref { $$ = lappend($1, $3); }
;
标识符table_ref可以定义为关系表达式、取别名的关系表达式、带括号的SELECT语句、表连接等形式。
由于FROM子句中子项(标识符table_ref的最简单和基本的形式是关系表达式(标识符relation_expr)。因此,下面分析relation_expr的语法定义。
relation_expr:
qualified_name
{
/* inheritance query, implicitly */
$$ = $1;
$$->inh = true;
$$->alias = NULL;
}
| qualified_name '*'
{
/* inheritance query, explicitly */
$$ = $1;
$$->inh = true;
$$->alias = NULL;
}
| ONLY qualified_name
{
/* no inheritance */
$$ = $2;
$$->inh = false;
$$->alias = NULL;
}
| ONLY '(' qualified_name ')'
{
/* no inheritance, SQL99-style syntax */
$$ = $3;
$$->inh = false;
$$->alias = NULL;
}
;
relation_expr_list:
relation_expr { $$ = list_make1($1); }
| relation_expr_list ',' relation_expr { $$ = lappend($1, $3); }
;
关系表达式relation_expr定义成qualified_name、带ONLY关键字的qualified_name等形式,最后qualified_name定义成relation_name。在成功匹配最终的标识符relation_name后,创建一个RangeVar结构体用来存储该关系的信息。
from_clause子句在分析树中同样被组织成一个List,每一个ListCell中包含一个RangeVar结构(或者其他结构)。
2.4 WHERE子句
WHERE子句中定义的是元组约束信息,对应着语法定义中的标识符where_clause。标识符where_clause定义为关键字WHERE和一个表达式(标识符a_expr)。
where_clause:
WHERE a_expr { $$ = $2; }
| /*EMPTY*/ { $$ = NULL; }
;
由于表达式是递归定义的,因此, A_Expr结构体中字段lexpr和rexpr分别代表操作符的左右两个子表达式,字段A-Expr_kind代表操作的类型。如果该表达式是常量或者属性等(表达式树中的叶子节点),则lexpr和rexpr都为NULL。在WHERE子句中,使用到的表的属性信息用ColumnRef结构体来组织。
2.5 GROUP BY子句
GROUP BY子句的作用是根据所指定的属性进行分组,对应着语法定义中的标识符group_clause, GROUP BY子句的语法结构与WHERE子句非常相似,在此不再详细讨论。Group by子句的语法定义如下:
group_clause:
GROUP_P BY group_by_list { $$ = $3; }
| /*EMPTY*/ { $$ = NIL; }
;
expr_list: a_expr
{
$$ = list_make1($1);
}
| expr_list ',' a_expr
{
$$ = lappend($1, $3);
}
;
2.6 HAVING子句和ORDER BY子句
HAVING子句的作用是根据指定的条件对GROUP BY的分组进行过滤,对应于having_clause标识符。ORDER BY子句的作用是根据指定属性对整个查询的结果进行排序,对应于sort_cluse标识符。Having子句和order by子句的语法定义如下:
having_clause:
HAVING a_expr { $$ = $2; }
| /*EMPTY*/ { $$ = NULL; }
;
sort_clause:
ORDER BY sortby_list { $$ = $3; }
;
sortby_list:
sortby { $$ = list_make1($1); }
| sortby_list ',' sortby { $$ = lappend($1, $3); }
;
sortby: a_expr USING qual_all_Op opt_nulls_order
{
$$ = makeNode(SortBy);
$$->node = $1;
$$->sortby_dir = SORTBY_USING;
$$->sortby_nulls = $4;
$$->useOp = $3;
$$->location = @3;
}
| a_expr opt_asc_desc opt_nulls_order
{
$$ = makeNode(SortBy);
$$->node = $1;
$$->sortby_dir = $2;
$$->sortby_nulls = $3;
$$->useOp = NIL;
$$->location = -1; /* no operator */
}
;