导读:欢迎来到 StarRocks 源码解析系列文章,我们将为你全方位揭晓 StarRocks 背后的技术原理和实践细节,助你逐步了解这款明星开源数据库产品。本期将主要介绍 StarRocks Parser 源码。
作者:刘航源 StarRocks Committer

概述

Parser 的主要工作是将字符串类型的 SQL 语句文本,解析成树形结构的抽象语法树(Abstract Syntax Tree,AST),便于语义解析和查询计划的生成与优化。本文首先介绍 StarRocks 中实现语法解析的工具 Antlr4,并介绍其在 StarRocks 内部的实现,以及相关的 AST 生成逻辑。

基本概念

在学习 Parser 之前,我们需要先了解一下相关的基本概念。

  1. 抽象语法树 (Abstract Syntax Tree,AST) :是 SQL 代码的一种抽象表示,以树的形状来表示语言的语法结构。抽象语法树一般用来进行代码语法的检查、代码风格的检查、代码的格式化、代码的高亮、代码的错误提示,以及代码的自动补全等。
  2. 词法分析器 (Lexer) :词法分析是指在计算机科学中,将字符序列转换为单词(Token)的过程。执行词法分析的程序便称为词法分析器,一般是供语法解析器(Parser)调用的。
  3. 语法解析器 (Parser):通常作为编译器或解释器出现。它的作用是进行语法检查,并构建由输入单词组成的数据结构(即抽象语法树)。语法解析器通常使用词法分析器(Lexer)从输入字符流中分离出一个个的单词,并将单词流作为其输入。在实际开发中,语法解析器可以手工编写,也可以使用工具自动生成。

ANTLR4

除了理解概念外,对工具的了解也是必不可少的。
Antlr4(Another Tool for Language Recognition)是一款基于 Java 开发的开源的语法分析器生成工具,能够根据语法规则文件生成对应的语法分析器,不仅广泛应用于 DSL 构建、语言词法语法解析等领域,在当下非常多流行的框架中也都有使用。

Antlr 可以生成不同 target 的 AST,包括 Java、C++、JS、Python、C# 等,满足不同语言的开发需求。具体来说,Antlr4 的优势主要包括如下五个方面:

  • 应用范围广:Antlr4 是目前主流的语法解析框架,目前市面上大多数计算引擎均将其作为解析工具(比如 Spark、Presto、Hive)。
  • 学习成本低:大量的学习资料和文档,甚至有持续维护的 SQL 语法文件,这为我们后续的开发提供非常大的便利。
  • 代码清晰易读:语法文件和代码逻辑分离,自动生成 Visitor 模式的代码。而且代码清晰易懂,不容易出错。
  • 周边产品完善:Anltr 可以在 ideaIDE 中直接根据语法文件和 sql 语句生成树状的图形,不仅方便调试,也能让我们清晰地看到自己的 SQL 是否出错,语法文件是否正确,从而提升开发效率。
  • 开放语法文件:可以开放 Antlr4 语法文件给第三方用户使用。因为没有代码逻辑,第三方可以方便地基于语法构建周边系统。
    StarRocks 的 antlr 词法定义文件和语法定义文件,分别定义在 StarRocksLex.g4 和 StarRocks.g4 两个文件中。有了对 Antlr4 工具的认识,接下来我们就来看看怎么在 StarRocks 中基于 Antlr4 实现语法解析,并生成 AST。

SqlParser

在 StarRocks 中,该怎么将 Parser 和 Antlr4 结合起来并在系统内部使用呢?
SqlParser 是 StarRocks 的 SQL 解析代码的入口,我们通过分析下面的代码,就能了解整个 Parser 的主要逻辑流程。

StarRocksLexer lexer = new StarRocksLexer(new CaseInsensitiveStream(CharStreams.fromString(sql)));
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
StarRocksParser parser = new StarRocksParser(tokenStream);
StarRocksParser.sqlMode = sqlMode;
parser.removeErrorListeners();
parser.addErrorListener(new ErrorHandler());
StarRocksParser.SqlStatementsContext sqlStatements = parser.sqlStatements();
StatementBase statement = (StatementBase) new AstBuilder().visitSingleStatement(sqlStatements.singleStatement(0));

首先,我们需要初始化 StarRocksLexer,即词法解析器。在这里,StarRocksLexer 是根据上文介绍的 StarRocksLex.g4 词法文件,使用 Antlr4 自动生成的代码类。

然后,代码将词法解析器 StarRocksLexer 作为参数,传入语法解析器中。语法解析器类StarRocksParser,同样是根据上文介绍的 StarRocks.g4 语法文件自动生成的代码类。

到这里,我们就完成了语法解析类的构建。之后再调用 parser.addErrorListener(new ErrorHandler()),将 Antlr4 的默认错误处理规则,替换为自定义的错误处理逻辑即可。

需要注意的是,调用 parser.sqlStatements() 返回值 StarRocksParser.SqlStatementsContext,这是一套 antlr 自定义的抽象语法树,根据语法文件生成。之后我们可以使用 AstBuilder,将 antlr 的语法树转换为 StarRocks 的抽象语法树(ASTBuilder 将在下一小节介绍)。

至此,整个 StarRocks Parser 的主流程结束,生成的 statement 将作为语义解析 Analyzer 的入参。值得注意的是,在例子中的第4行,代码对 StarRocksParser 赋值了一个 SQL_MODE。在不同的 SQL_MODE 下,相同的 sql 语句可能代表不同的逻辑。

再补充一句,当前涉及语法方面的 SQL_MODE,StarRocks 仅支持 PIPES_AS_CONCAT。如果设置了PIPES_AS_CONCAT,“||”将被解析为字符串 concat 函数,否则"||"代表逻辑或。而 Parser 在解析之前,会根据 session variable 中设置的 sql_mode 来判断将要使用何种方式来解析。

ASTBuilder

在主流程中,StarRocksLexer 和 StarRocksParser 分别基于 StarRocksLex.g4 和 StarRocks.g4 文件,由 Antlr4 自动翻译并生成代码。而 AstBuilder 则需要手动编写的代码模块,这也是 Parser 的逻辑重点。

下面将介绍 AstBuilder 的整体逻辑,我们以 showTables 和 ShowDatabases 语句为例,语法文件如下:

singleStatement: SHOW FULL? TABLES ((FROM | IN) db=qualifiedName)?((LIKE pattern=string) | (WHERE expression))?                #showTables| SHOW DATABASES ((LIKE pattern=string) | (WHERE expression))?   #showDatabases

antlr 的语法文件采用 BNF 范式,用'|'表示分支选项,'?'表达0次或一次,其他符号可类比正则表达式。比如SHOW FULL? 表示在 showTables 中,SHOW 关键字是必须的,而 FULL 则是一个可选择项。

Antlr4 会根据语法文件生成一份 Visitor 模式的代码,这样就可以做到动作代码与文法产生式解耦,利于文法产生式的重用。而自定义的 AstBuilder 文件则继承了 StarRocksBaseVisitor,用于将 antlr 内部的 AST 翻译成 StarRocks 自定义的 AST。

我们依旧以 show table 和 show database 语法为例,代码如下:

public class AstBuilder extends StarRocksBaseVisitor<ParseNode> {@Overridepublic ParseNode visitSingleStatement(StarRocksParser.SingleStatementContext context) {return visit(context.statement());}// -------------------------------- Statement ------------------------------@Overridepublic ParseNode visitShowDatabases(StarRocksParser.ShowDatabasesContext context) {if (context.pattern != null) {StringLiteral stringLiteral = (StringLiteral) visit(context.pattern);return new ShowDbStmt(stringLiteral.getValue());} else if (context.expression() != null) {return new ShowDbStmt(null, (Expr) visit(context.expression()));} else {return new ShowDbStmt(null, null);}}@Overridepublic ParseNode visitShowTables(StarRocksParser.ShowTablesContext context) {boolean isVerbose = context.FULL() != null;String database = null;if (context.db != null) {database = context.db.getText();}if (context.pattern != null) {StringLiteral stringLiteral = (StringLiteral) visit(context.pattern);return new ShowTableStmt(database, isVerbose, stringLiteral.getValue());} else if (context.expression() != null) {return new ShowTableStmt(database, isVerbose, null, (Expr) visit(context.expression()));} else {return new ShowTableStmt(database, isVerbose, null);}}

可以看到,Antlr 采用递归下降文法,所有的解析流程都是从上到下的,从树的根节点进行解析,一直解析到树的叶子节点。singleStatement 为所有 sql statement 的根,singleStatement 可能根据 statement 的类型不同,访问不同的 visitXXX,本例即 visitShowDatabases 或 visitShowTables。

而 Antlr 则依据我们语法文件中的第3、4行后面的#注释,生成了不同的 visit 函数。#后面的注释,在Antlr 中表示为前面的一长串语法文件“起个名字”。而在相应的 visit 函数中,开发者可以根据自身的需要,将 statement 独有的代码封装在一个函数中。

这样做,就起到了语法文件与语法解析代码充分解耦合的作用。而 visit 的返回值返回一个 AST 的基类,在StarRocks 中称为 ParseNode。至此,我们就生成了 StarRocks 内部需要的抽象语法树,Parser 模块的所有功能结束。

小结

今天我们对 Parser 源码进行了重点分析,包括 ANTLR4、SqlParser 和 ASTBuilder。与此同时,我们还通过一个例子,介绍了如何将一条文本类型的 SQL 语句,一步步解析成 StarRocks 内部使用的 AST。

可以看到,Parser 能判断出用户的 SQL 中是否存在明显的语法错误,如 SQL 语句select * from;会在Parser 阶段报错。但如果 SQL 语句select * from foo;没有语法错误,StarRocks 中也没有 foo 这张表,那么 StarRocks 该如何做到错误处理呢?这就需要依赖下一节的 Analyzer 模块去判断了。

本期 StarRocks 源码解析到这就结束了,好学的你肯定学会了一些新东西,又产生了一些新困惑,不妨留言评论或者加入我们的社区一起交流(StarRocks 小助手微信号)。下一篇 StarRocks 源码解析,我们将为你带来 Analyzer 源码解析。

References

Antlr4官方指南
Antlr4官方示例:Grammars-v4
https://pragprog.com/titles/tpa

StarRocks Parser 源码解析相关推荐

  1. StarRocks Analyzer 源码解析

    导读:欢迎来到 StarRocks 源码解析系列文章,我们将为你全方位揭晓 StarRocks 背后的技术原理和实践细节,助你逐步了解这款明星开源数据库产品.本期将主要介绍 StarRocks Par ...

  2. StarRocks Join Reorder 源码解析

    导读:欢迎来到 StarRocks 源码解析系列文章,我们将为你全方位揭晓 StarRocks 背后的技术原理和实践细节,助你逐步了解这款明星开源数据库产品. 本期 StarRocks 技术内幕将介绍 ...

  3. 【vuejs深入三】vue源码解析之二 htmlParse解析器的实现

    写在前面 一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残. 昨天博主分析了一下在vue中,最为基础核心的api,parse函数,它的作用是将vue的模板字符串转换成ast,从而 ...

  4. 渣渣菜鸡的 ElasticSearch 源码解析 —— 启动流程(上)

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/08/11/es-code02/ 前提 上篇文章写了 ElasticSearch 源码解析 -- ...

  5. sharding-jdbc源码解析之sql解析

    2019独角兽企业重金招聘Python工程师标准>>> 说在前面 本文转自"天河聊技术"微信公众号 本次介绍的是sharding-jdbc的源码解析部分的sql解 ...

  6. Android LayoutInflater源码解析:你真的能正确使用吗?

    版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 好久没写博客了,最近忙着换工作,没时间写,工作刚定下来.稍后有时间会写一下换工作经历.接下来进入本篇主题,本来没想写LayoutInflater的 ...

  7. Mybatis运行原理及源码解析

    Mybatis源码解析 一.前言 本文旨在mybatis源码解析,将整个mybatis运行原理讲解清楚,本文代码地址: https://github.com/lchpersonal/mybatis-l ...

  8. Spring @Import源码解析

    在Spring boot中常用到@Import,允许通过它引入 @Configuration 注解的类 (java config), 引入ImportSelector接口(这个比较重要, 因为要通过它 ...

  9. Mybatis 源码解析 -- 基于配置的源码解析(二)

    为什么80%的码农都做不了架构师?>>>    mapper解析 接着上篇的配置,本篇主要讲解mappers标签 <?xml version="1.0" e ...

最新文章

  1. 让你二十年后仍是人才
  2. [蓝桥杯2016初赛]卡片换位 bfs+set
  3. R语言构建xgboost模型:自定义损失函数(目标函数、loss function、object function)、评估函数(evaluation function)
  4. jpa之PagingAndSortingRepository带分页查询
  5. java中使用openssl生成的rsa公私钥进行数据加解密_使用openssl生成RSA公钥和私钥对...
  6. OAuth 2.1 带来了哪些变化
  7. 小程序员的大梦想 与盖茨像哥们儿
  8. SCOM 2016 配置报警邮件 (下)
  9. 导师评价网北工大计算机段老师,北京工业大学怎么样好不好(排名-师资-专业-评价)...
  10. ESB和注册服务管理的区别
  11. maven项目在eclipse中debug
  12. spring 默认情况下事务是惟一的 同一个方法里面第一个sql开启后 在执行完 将事务传递给下一个sql...
  13. HTML5生日祝福网页制作 (粉色系列为你定制) HTML+CSS+JavaScript
  14. android黑色半透明dialog背景,Dialog背景半透明
  15. 等保2.0二级通用要求
  16. 爬虫--Item Pipeline 介绍(21)
  17. 三菱FX系列控制步进电机回原点方向
  18. 阳光事业必须在阳光下进行
  19. 报志愿时计算机一大类分数,高考出分在即 测一测你的分数可以上哪些大学?...
  20. FTP暴力破解-Hydra

热门文章

  1. 什么是编码?什么是解码?
  2. linux系统游戏性能对比,Steam Mac/Windows游戏性能、画质对比
  3. dataguard 日志的应用
  4. JAVA 导出Excel 单元格合并
  5. 【Linux】软件包管理器yum和编辑器vim(部分动图演示)
  6. 可移植bit-field与driverlib兼容的C2000控制器工程模板创建及SysConfig配置
  7. PCTA考试经验分享
  8. CAD中插入外部参照字体会变繁体_为什么CAD插入相同图框但尺寸却相差很多?
  9. BugKu-图穷匕见
  10. 二、八、十、十六进制之间的转换