写点什么

Presto 设计与实现(十):SQL 语法分析

作者:冰心的小屋
  • 2023-08-27
    北京
  • 本文字数:1752 字

    阅读完需:约 6 分钟

Presto 设计与实现(十):SQL 语法分析

SQL 语句经过词法分析解析后,会转化成 Token 序列作为语法分析器的输入,语法分析器加载所有的语法规则,根据内部定义的解析策略对 Token 序列进行解析,对于 SQL 查询语句,常见的语法错误:


  • 关键字拼写错误;

  • 关键字先后顺序错误;

  • 关键字、UNRECOGNIZED 、表达式和函数之间缺少空格;

  • 使用了未知的函数名称、特殊符号或某些符号使用了全角;

  • 分号或括号未成对出现。


下面是 SqlBase.g4 文件中关于 SQL 查询语法规则定义的部分片段:

# 匹配 SQL 查询片段querySpecification    : SELECT setQuantifier? selectItem (',' selectItem)* # 带分隔符的序列模式      (FROM relation (',' relation)*)?                   # 嵌套模式      (WHERE where=booleanExpression)?      (GROUP BY groupBy)?      (HAVING having=booleanExpression)?    ;
# 选择模式:从 DISTINCT 和 ALL 选择 1 个setQuantifier : DISTINCT | ALL ;
selectItem : expression (AS? identifier)? # 别名 | qualifiedName '.' ASTERISK # 某个表的所有列 | ASTERISK # 所有列 ;
expression : booleanExpression ; # 带分隔符的序列模式qualifiedName : identifier ('.' identifier)* ; # 选择模式identifier : IDENTIFIER | QUOTED_IDENTIFIER | nonReserved | BACKQUOTED_IDENTIFIER | DIGIT_IDENTIFIER ; IDENTIFIER : (LETTER | '_') (LETTER | DIGIT | '_' | '@' | ':')* ; fragment DIGIT : [0-9] ;
fragment LETTER : [A-Z] ;
relation : left=relation ( CROSS JOIN right=sampledRelation | joinType JOIN rightRelation=relation joinCriteria | NATURAL joinType JOIN right=sampledRelation ) | sampledRelation ; sampledRelation : aliasedRelation ( TABLESAMPLE sampleType '(' percentage=expression ')' )? ; aliasedRelation : relationPrimary (AS? identifier columnAliases?)? ; # 选择模式relationPrimary : qualifiedName | '(' query ')' | UNNEST '(' expression (',' expression)* ')' (WITH ORDINALITY)? | LATERAL '(' query ')' | '(' relation ')' ;
复制代码


将上面的语法规则转为思维导图,可以直观的查看语法规则的详细分支:


上篇文章解析的 Token 序列,结合这个思维导图,下面是语法分析器使用 LL 最左推导由上到下的解析过程:



  1. 语法分析器首先会加载所有已定义的语法规则;

  2. 对 Token 序列进行遍历解析;

  3. 语法分析器按照默认的语法规则向下进行匹配;

  4. 首个 Token 匹配到 SELECT 非保留字,接着会对 id,name,address,age 7 个 Token 进行校验:匹配到路径 selectItem (',' selectItem)* -> selectItem -> booleanExpression -> valueExpression -> primaryExpression -> identifier -> IDENTIFIER -> LETTER,校验通过;

  5. 匹配到 FROM 非保留字,接着会对 mysql.ice.user 进行校验: 匹配到路径 sampledRelation -> aliasedRelation -> relationPrimary -> qualifiedName -> identifier ('.' identifier)*,校验通过;

  6. 匹配到 WEREH 非保留字,接着会匹配后面的表达式;

  7. 遍历到末尾,解析工作结束。


本系列文章:

Presto 设计与实现(一):开篇

Presto 设计与实现(二):一切从 0 开始?

Presto 设计与实现(三):依赖注入框架 Guice

Presto 设计与实现(四):动态代码生成 ByteBuddy

Presto 设计与实现(五):自动配置

Presto 设计与实现(六):JMX

Presto 设计与实现(七):Event

Presto 设计与实现(八):Presto JDBC

Presto 设计与实现(九):SQL 词法分析

Presto 设计与实现(十):SQL 语法分析

Presto 设计与实现(十一):抽象语法树 AST

Presto 设计与实现(十二):SQL 逻辑计划

发布于: 2023-08-27阅读数: 23
用户头像

分享技术上的点滴收获! 2013-08-06 加入

一杯咖啡,一首老歌,一段代码,欢迎做客冰屋,享受编码和技术带来的快乐! 欢迎关注公众号:冰心的小屋

评论

发布
暂无评论
Presto 设计与实现(十):SQL 语法分析_数据湖_冰心的小屋_InfoQ写作社区