老叶茶馆|ClickHouse和他的朋友们(8)纯手工打造的SQL解析器( 三 )


if(!parser.parse(pos, list_node, expected))
returnfalse;
...
parser.parse 里又调用 第四层ParserSelectQuery::parseImpl 状态空间:
bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
{
auto select_query = std::make_shared<ASTSelectQuery>;
node = select_query;
ParserKeyword s_select( "SELECT");
ParserKeyword s_distinct( "DISTINCT");
ParserKeyword s_from( "FROM");
ParserKeyword s_prewhere( "PREWHERE");
ParserKeyword s_where( "WHERE");
ParserKeyword s_group_by( "GROUP BY");
ParserKeyword s_with( "WITH");
ParserKeyword s_totals( "TOTALS");
ParserKeyword s_having( "HAVING");
ParserKeyword s_order_by( "ORDER BY");
ParserKeyword s_limit( "LIMIT");
ParserKeyword s_settings( "SETTINGS");
ParserKeyword s_by( "BY");
ParserKeyword s_rollup( "ROLLUP");
ParserKeyword s_cube( "CUBE");
ParserKeyword s_top( "TOP");
ParserKeyword s_with_ties( "WITH TIES");
ParserKeyword s_offset( "OFFSET");
ParserNotEmptyExpressionList exp_list( false);
ParserNotEmptyExpressionList exp_list_for_with_clause( false);
ParserNotEmptyExpressionList exp_list_for_select_clause( true);
...
if(!exp_list_for_select_clause.parse(pos, select_expression_list, expected))
returnfalse;
第五层exp_list_for_select_clause.parse ParserExpressionList::parseImpl状态空间继续:
bool ParserExpressionList::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
{
returnParserList(
std::make_unique<ParserExpressionWithOptionalAlias>(allow_alias_without_as_keyword),
std::make_unique<ParserToken>(TokenType::Comma))
.parse(pos, node, expected);
}
... ... 写不下去个鸟!
可以发现 , ast parser 的时候 , 预先构造好状态空间 , 比如 select 的状态空间:

  1. expression list
  2. from tables
  3. where
  4. group by
  5. with ...
  6. order by
  7. limit
在一个状态空间內 , 还可以根据 parse 返回的 bool 判断是否继续进入子状态空间 , 一直递归解析出整个 ast 。
总结
手工 parser 的好处是代码清晰简洁 , 每个细节可防可控 , 以及友好的错误处理 , 改动起来不会一发动全身 。 缺点是手工成本太高 , 需要大量的测试来保证其正确性 , 还需要一些fuzz来保证可靠性 。 好在ClickHouse 已经实现的比较全面 , 即使有新的需求 , 在现有基础上修修补补即可 。
文内链接