当前位置:网站首页>学习Apache ShardingSphere解析器源码(一)
学习Apache ShardingSphere解析器源码(一)
2022-08-10 23:44:00 【InfoQ】
1. 写作由来
Apache Shardingsphere是一款国产精品中间件。我对这款中间件关注了很久了,刚好最近也业务中需要用到Antlr4来对语言进行解析,作为学习资料,我仔细的研究了ShardingSphere的解析器部分源码。通过对其源码的阅读和仿写,我获得了丰富的Antlr4使用经验,同时,我也尝试着向官方贡献了自己写的一点解析代码,并被官方采纳,也是感到无比的高兴。
2. 以Oracle的DropTable功能为例分析
SharingSphere在其Github上提供了Oracle的Antlr4的g4文件,这个文件中支持了DDL的大部分功能,但是鉴于Oracle本身的功能复杂性,还是有一些功能没有提供的,但是也不影响学习。至于如何用g4文件生成代码,这不是本文的目的,有机会另开一篇来写。这里就默认生成了所有需要的AST和访问器代码。如下图:

Antlr4会自动生成这些代码,注意其中的OracleStatementBaseVisitor.java,我们所有的操作都要通过继承它来实现。换句话说,我们需要自己编写遍历AST的逻辑。首先来看看DropTable功能在g4文件中的定义:
dropTable
: DROP TABLE tableName (CASCADE CONSTRAINTS)? (PURGE)?
;
tableName
: (owner DOT_)? name
;
owner
: identifier
;
name
: identifier
;
identifier
: IDENTIFIER_ | unreservedWord
;
再看测试看看这样一句SQL会被解析成一棵什么样形态的树:
-- 删除用户tom下的jerry表
drop table tom.jerry;

从上图中可以看出,我们只需要去访问tableName这个节点下的所有节点就可以拿到所有的有用信息。解析SQL第一步,我们需要对模型进行抽象。ShardingSphere的策略是所有的对象都实现自一个叫做ASTNode的接口:
public interface ASTNode {}
接下来继续观察上面的树,我们可以发现owner和name都是一个叫做identifier的东西,这个identifier实际上是一个字符串或者是一个用引号包裹起来的字符串。那么为了保存它,需要编写一个类来保存,为了简单,我们就不考虑保存引号了:
public class IdentifierValue implements ASTNode {
private final String name;
public IdentifierValue(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
这个基本的问题解决之后,就可以开始着手解决如何保存name和owner的问题了,还是观察上图,发现这两个token都是identifier,因此基本上可以不用做额外操作。接下来看看最关键的节点tableName,它包括了两个元素owner和name,其中owner是可选的元素,在知道了这一点之后,我们设计这样一个类:
public class TableName implements ASTNode {
private IdentifierValue owner;
private final IdentifierValue name;
public TableName(IdentifierValue name) {
this.name = name;
}
public void setOwner(IdentifierValue owner) {
this.owner = owner;
}
public Optional<IdentifierValue> getOwner() {
return Optional.ofNullable(owner);
}
public IdentifierValue getName() {
return name;
}
}
最后,还需要对语句本身进行抽象:
public class DropTableStatement implements ASTNode {
private final TableName tableName;
public DropTableStatement(TableName tableName) {
this.tableName = tableName;
}
public TableName getTableName() {
return tableName;
}
@Override
public String toString() {
return "DropTableStatement{" +
"tableName=" + tableName +
'}';
}
}
这样,我们也就具备了将这个语句中所有的元素保存成模型的可能了(这里我忽略了(CASCADE CONSTRAINTS)? (PURGE)?后缀,因为这不过是一个简单的判断)。要将语句转换成模型,需要编写实现visitor逻辑,对这棵树的每一个节点进行遍历操作:
public class OracleDDLStatementVisitor extends OracleStatementBaseVisitor<ASTNode> {
@Override
public ASTNode visitDropTable(OracleStatementParser.DropTableContext ctx) {
return new DropTableStatement((TableName) visit(ctx.tableName()));
}
@Override
public ASTNode visitTableName(OracleStatementParser.TableNameContext ctx) {
TableName result = new TableName((IdentifierValue) visit(ctx.name()));
if (Objects.nonNull(ctx.owner())) {
result.setOwner((IdentifierValue) visit(ctx.owner()));
}
return result;
}
@Override
public ASTNode visitIdentifier(OracleStatementParser.IdentifierContext ctx) {
return new IdentifierValue(ctx.IDENTIFIER_().getText());
}
}
由于语句比较简单,只需要遍历三个结点就可以了,这就有了上面的逻辑。经过下面这段代码的测试,我的代码是没有问题的。
边栏推荐
猜你喜欢
随机推荐
sqlmap结合dnslog快速注入
Three-column layout implementation
2.0966 铝青铜板CuAl10Ni5Fe4铜棒
如何做专利挖掘,关键是寻找专利点,其实并不太难
SQL注入基础---order by \ limit \ 宽字节注入
14. Thymeleaf
三栏布局实现
ASIO4ALL是什么
web 性能提升(将持续更新……)
关于弱监督学习的详细介绍——A Brief Introduction to Weakly Supervised Learning
安科瑞为工业能效行动计划提供EMS解决方案-Susie 周
卷积神经网络CNN详细介绍
开源一夏 | 参与开源能让人更幸福
给肯德基打工的调料商,年赚两亿
SQL injection base - order by injection, limit, wide byte
ROS实验笔记之——UZH-FPV数据集的验证
【C语言】数据储存详解
[C language] Implementation of guessing number game
oai 采样频率计算
HPb59-1铅黄铜