SelectStmt: select_no_parens %prec UMINUS| select_with_parens %prec UMINUS
select_with_parens:'(' select_no_parens ')' { $$ = $2; }| '(' select_with_parens ')' { $$ = $2; }
该规则返回单个SelectStmt节点或它们的树,表示集合操作树(set-operation tree)。The rule returns either a single SelectStmt node or a tree of them, representing a set-operation tree. 当sub-SELECT位于a_expr内并且有多余的括号时,会出现歧义:括号是属于子SELECT还是属于周围的a_exp?我们不在乎,但bison想知道。There is an ambiguity when a sub-SELECT is within an a_expr and there are excess parentheses: do the parentheses belong to the sub-SELECT or to the surrounding a_expr? We don’t really care, but bison wants to know. 为了解决歧义,我们会谨慎地定义语法,以便尽可能长时间地推迟决策:只要我们能继续在sub-SELECT中吸收括号,我们就会这样做,只有当不再可能这样做时,我们才会决定括号属于表达式。To resolve the ambiguity, we are careful to define the grammar so that the decision is staved off as long as possible: as long as we can keep absorbing parentheses into the sub-SELECT, we will do so, and only when it’s no longer possible to do that will we decide that parens belong to the expression. 例如,在“SELECT(((SELECT 2))+3)
”中,额外的括号被视为sub-select的一部分。“SELECT (((SELECT 2)) UNION SELECT 2)
”显示了这样做的必要性。如果我们将“((SELECT 2))
”解析为a_expr
,那么当我们看到UNION时再返回SELECT视点就太晚了。For example, in “SELECT (((SELECT 2)) + 3)
” the extra parentheses are treated as part of the sub-select. The necessity of doing it that way is shown by “SELECT (((SELECT 2)) UNION SELECT 2)
”. Had we parsed “((SELECT 2))
” as an a_expr
, it’d be too late to go back to the SELECT viewpoint when we see the UNION.
此方法通过定义一个非终端select_with_parens来实现,它表示至少有一个外层括号的select,并注意在表达式语法中使用select_wwith_parens,never ‘(’ SelectStmt ‘)’。然后我们将有shift-reduce冲突,我们可以解决这些冲突,以便始终将’(’
select_no_parens:simple_select { $$ = $1; }
此规则解析集合操作(set operations)中可能出现的SELECT语句,包括UNION、INTERSECT和EXCEPT。'(‘和’)‘可用于指定集合操作的顺序。如果没有’(‘和)’,我们希望操作按照此文件开头的优先级规范进行排序。与select_no_parens一样,simple_select不能有外括号,但可以有带括号的子句。This rule parses SELECT statements that can appear within set operations, including UNION, INTERSECT and EXCEPT. ‘(’ and ‘)’ can be used to specify the ordering of the set operations. Without ‘(’ and ‘)’ we want the operations to be ordered per the precedence specs at the head of this file. As with select_no_parens, simple_select cannot have outer parentheses, but can have parenthesized subclauses.
请注意,排序子句不能包含在此级别–SQL需要 Note that sort clauses cannot be included at this level — SQL requires
SELECT foo UNION SELECT bar ORDER BY baz
要解析为
(SELECT foo UNION SELECT bar) ORDER BY baz
而不是
SELECT foo UNION (SELECT bar ORDER BY baz)
对于WITH、for UPDATE和LIMIT也是如此。因此,这些子句被描述为select_no_parens生成的一部分,而不是simple_select。这并不限制功能,因为您可以在括号内重新引入这些子句。注意:只有最左边的组件SelectStmt应该具有INTO。然而,语法没有检查这一点;解析分析必须检查它。Likewise for WITH, FOR UPDATE and LIMIT. Therefore, those clauses are described as part of the select_no_parens production, not simple_select. This does not limit functionality, because you can reintroduce these clauses inside parentheses. NOTE: only the leftmost component SelectStmt should have INTO. However, this is not checked by the grammar; parse analysis must check it.
simple_select:SELECT opt_all_clause opt_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; }| TABLE relation_expr /* same as SELECT * FROM relation_expr */{ ColumnRef *cr = makeNode(ColumnRef); cr->fields = list_make1(makeNode(A_Star)); cr->location = -1; ResTarget *rt = makeNode(ResTarget); rt->name = NULL; rt->indirection = NIL; rt->val = (Node *)cr; rt->location = -1;SelectStmt *n = makeNode(SelectStmt); n->targetList = list_make1(rt); n->fromClause = list_make1($2);$$ = (Node *)n;}| select_clause UNION all_or_distinct select_clause // 集合操作(set operations) UNION{ $$ = makeSetOp(SETOP_UNION, $3, $1, $4); }| select_clause INTERSECT all_or_distinct select_clause // 集合操作(set operations) SETOP_INTERSECT{ $$ = makeSetOp(SETOP_INTERSECT, $3, $1, $4); }| select_clause EXCEPT all_or_distinct select_clause // 集合操作(set operations) SETOP_EXCEPT{ $$ = makeSetOp(SETOP_EXCEPT, $3, $1, $4); }
select_no_parens的其他规则主要是处理sort_clause、opt_sort_clause、select_limit、opt_select_limit、for_locking_clause、opt_for_locking_clause、with_clause等规则,使用insertSelectOptions函数将这些语句生成的节点插入到SelectStmt对应的成员中。
select_clause:simple_select { $$ = $1; }| select_with_parens { $$ = $1; }
select_no_parens:select_clause sort_clause{ insertSelectOptions((SelectStmt *) $1, $2, NIL, NULL, NULL, NULL, yyscanner); $$ = $1; }| select_clause opt_sort_clause for_locking_clause opt_select_limit{ insertSelectOptions((SelectStmt *) $1, $2, $3, list_nth($4, 0), list_nth($4, 1), NULL, yyscanner); $$ = $1; }| select_clause opt_sort_clause select_limit opt_for_locking_clause{ insertSelectOptions((SelectStmt *) $1, $2, $4, list_nth($3, 0), list_nth($3, 1), NULL, yyscanner); $$ = $1; }| with_clause select_clause{ insertSelectOptions((SelectStmt *) $2, NULL, NIL, NULL, NULL, $1, yyscanner); $$ = $2; }| with_clause select_clause sort_clause{ insertSelectOptions((SelectStmt *) $2, $3, NIL, NULL, NULL, $1, yyscanner); $$ = $2; }| with_clause select_clause opt_sort_clause for_locking_clause opt_select_limit{ insertSelectOptions((SelectStmt *) $2, $3, $4, list_nth($5, 0), list_nth($5, 1), $1, yyscanner); $$ = $2; }| with_clause select_clause opt_sort_clause select_limit opt_for_locking_clause{ insertSelectOptions((SelectStmt *) $2, $3, $5, list_nth($4, 0), list_nth($4, 1), $1, yyscanner); $$ = $2; }
insertSelectOptions() Insert ORDER BY, etc into an already-constructed SelectStmt. This routine is just to avoid duplicating code in SelectStmt productions. 主要包含sortClause【ORDER BY】、lockingClause【FOR UPDATE | FOR NO KEY UPDATE | FOR SHARE | FOR KEY SHARE】、limitOffset、limitCount、withClause【WITH | WITH_LA | WITH RECURSIVE】子句结构体。
/* insertSelectOptions() Insert ORDER BY, etc into an already-constructed SelectStmt. This routine is just to avoid duplicating code in SelectStmt productions. */
static void insertSelectOptions(SelectStmt *stmt, List *sortClause, List *lockingClause, Node *limitOffset, Node *limitCount, WithClause *withClause, core_yyscan_t yyscanner) {/* Tests here are to reject constructs like (SELECT foo ORDER BY bar) ORDER BY baz */if (sortClause) { // 不允许多重ORDER BYif (stmt->sortClause) ereport(ERROR,(errcode(ERRCODE_SYNTAX_ERROR), errmsg("multiple ORDER BY clauses not allowed"), parser_errposition(exprLocation((Node *) sortClause))));stmt->sortClause = sortClause;}/* We can handle multiple locking clauses, though */stmt->lockingClause = list_concat(stmt->lockingClause, lockingClause);if (limitOffset){ // 不允许多重OFFSETif (stmt->limitOffset) ereport(ERROR,(errcode(ERRCODE_SYNTAX_ERROR),errmsg("multiple OFFSET clauses not allowed"),parser_errposition(exprLocation(limitOffset))));stmt->limitOffset = limitOffset;}if (limitCount){ // 不允许多重LIMITif (stmt->limitCount) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("multiple LIMIT clauses not allowed"), parser_errposition(exprLocation(limitCount))));stmt->limitCount = limitCount;}if (withClause){ // 不允许多重WITHif (stmt->withClause)ereport(ERROR,(errcode(ERRCODE_SYNTAX_ERROR), errmsg("multiple WITH clauses not allowed"), parser_errposition(exprLocation((Node *) withClause))));stmt->withClause = withClause;}
}
从如下transformStmt函数可以看出,对于SelectStmt的处理分为了三种情况:ValuesClause;集合操作(set operations)【select_clause UNION/INTERSECT/EXCEPT all_or_distinct select_clause】;其他正常SelectStmt。
Query *transformStmt(ParseState *pstate, Node *parseTree){Query *result;switch (nodeTag(parseTree)){ ...case T_SelectStmt:{SelectStmt *n = (SelectStmt *) parseTree;if (n->valuesLists)result = transformValuesClause(pstate, n);else if (n->op == SETOP_NONE)result = transformSelectStmt(pstate, n);elseresult = transformSetOperationStmt(pstate, n);}break; ...} result->querySource = QSRC_ORIGINAL; /* Mark as original query until we learn differently */result->canSetTag = true;return result;
}