sharding-jdbc对于sql的解析需要依赖解析引擎,解析过程会经历两大过程:
1 通过G4规则的解析器生成抽象语法树;
2 通过SQL解析引擎输出解析结果,这一步包含SQL提取、SQL填充、SQL优化。
抽象语法树
解析过程分为词法解析和语法解析。 词法解析器用于将SQL拆解为不可再分的原子符号,称为Token。并根据不同数据库方言所提供的字典,将其归类为关键字,表达式,字面量和操作符。 再使用语法解析器将SQL转换为抽象语法树。最后,通过对抽象语法树的遍历去提炼分片所需的上下文,并标记有可能需要改写的位置。 供分片使用的解析上下文包含查询选择项(Select Items)、表信息(Table)、分片条件(Sharding Condition)、自增主键信息(Auto increment Primary Key)、排序信息(Order By)、分组信息(Group By)以及分页信息(Limit、Rownum、Top)。 SQL的一次解析过程是不可逆的,一个个Token的按SQL原本的顺序依次进行解析,性能很高。 考虑到各种数据库SQL方言的异同,在解析模块提供了各类数据库的SQL方言字典。
SQL解析引擎
第三代SQL解析器则从3.0.x版本开始,ShardingSphere尝试使用ANTLR作为SQL解析的引擎,并计划根据DDL -> TCL -> DAL –> DCL -> DML –>DQL这个顺序,依次替换原有的解析引擎,目前仍处于替换迭代中。 使用ANTLR的原因是希望ShardingSphere的解析引擎能够更好的对SQL进行兼容。对于复杂的表达式、递归、子查询等语句,虽然ShardingSphere的分片核心并不关注,但是会影响对于SQL理解的友好度。 经过实例测试,ANTLR解析SQL的性能比自研的SQL解析引擎慢3-10倍左右。为了弥补这一差距,ShardingSphere将使用PreparedStatement的SQL解析的语法树放入缓存。 因此建议采用PreparedStatement这种SQL预编译的方式提升性能。
解析相应的源码如下:
//Antlr解析引擎
public final class AntlrParsingEngine implements SQLParser {
//SQL解析器
private final SQLParserEngine parserEngine;
//SQL提取器
private final SQLSegmentsExtractorEngine extractorEngine;
//SQL填充器
private final SQLStatementFillerEngine fillerEngine;
//SQL优化器
private final SQLStatementOptimizerEngine optimizerEngine;
public AntlrParsingEngine(final DatabaseType databaseType, final String sql, final ShardingRule shardingRule, final ShardingTableMetaData shardingTableMetaData) {
parserEngine = new SQLParserEngine(databaseType, sql);
extractorEngine = new SQLSegmentsExtractorEngine();
fillerEngine = new SQLStatementFillerEngine(sql, shardingRule, shardingTableMetaData);
optimizerEngine = new SQLStatementOptimizerEngine(shardingTableMetaData);
}
@Override
public SQLStatement parse() {
//1 通过SQL解析器解析,得到抽象语法树
SQLAST ast = parserEngine.parse();
//2 通过SQL提取器解析,得到SQL片段集合
Collection<SQLSegment> sqlSegments = extractorEngine.extract(ast);
//3 通过SQL填充器,得到SQL解析结果
SQLStatement result = fillerEngine.fill(sqlSegments, ast.getRule());
//4 通过优化器进一步优化SQL解析结果
optimizerEngine.optimize(ast.getRule(), result);
return result;
}
}
SQL解析器
@RequiredArgsConstructor
public final class SQLParserEngine {
//解析规则注册表
private final ParsingRuleRegistry parsingRuleRegistry = ParsingRuleRegistry.getInstance();
//数据库类型
private final DatabaseType databaseType;
//需要解析的SQL
private final String sql;
/**
* Parse SQL to AST.
*
* @return Abstract syntax tree of SQL
*/
public SQLAST parse() {
// 通过数据库类型获取指定数据库SQL解析器的实例,执行sql的解析,获取解析结果
ParseTree parseTree = SQLParserFactory.newInstance(databaseType, sql).execute().getChild(0);
if (parseTree instanceof ErrorNode) {
throw new SQLParsingUnsupportedException(String.format("Unsupported SQL of `%s`", sql));
}
//从SQL解析规则注册表中获取解析规则
Optional<SQLStatementRule> sqlStatementRule = parsingRuleRegistry.findSQLStatementRule(databaseType, parseTree.getClass().getSimpleName());
if (!sqlStatementRule.isPresent()) {
throw new SQLParsingUnsupportedException(String.format("Unsupported SQL of `%s`", sql));
}
//返回抽象语法树
return new SQLAST((ParserRuleContext) parseTree, sqlStatementRule.get());
}
}
SQL提取器
public final class SQLSegmentsExtractorEngine {
/**
* Extract SQL segments.
*
* @param ast SQL AST
* @return SQL segments
*/
public Collection<SQLSegment> extract(final SQLAST ast) {
Collection<SQLSegment> result = new LinkedList<>();
//遍历抽象语法树中SQL语法规则中的(SQLSegmentExtractor)SQL片段提取器
for (SQLSegmentExtractor each : ast.getRule().getExtractors()) {
//
if (each instanceof OptionalSQLSegmentExtractor) {
//OptionalSQLSegmentExtractor提取器提取的SQL片段为单个
Optional<? extends SQLSegment> sqlSegment = ((OptionalSQLSegmentExtractor) each).extract(ast.getParserRuleContext());
if (sqlSegment.isPresent()) {
result.add(sqlSegment.get());
}
}
//CollectionSQLSegmentExtractor提取器提取的SQL片段为SQL片段集合
if (each instanceof CollectionSQLSegmentExtractor) {
result.addAll(((CollectionSQLSegmentExtractor) each).extract(ast.getParserRuleContext()));
}
}
return result;
}
}
SQL填充器
@RequiredArgsConstructor
public final class SQLStatementFillerEngine {
//解析规则注册表
private final ParsingRuleRegistry parsingRuleRegistry = ParsingRuleRegistry.getInstance();
private final String sql;
// 数据库和表分片规则配置
private final ShardingRule shardingRule;
//分片表元数据
private final ShardingTableMetaData shardingTableMetaData;
/**
* Fill SQL statement.
*
* @param sqlSegments SQL segments
* @param rule SQL statement rule
* @return SQL statement
*/
@SuppressWarnings("unchecked")
//避免javac捕获或抛出方法主体中的语句声明它们生成的任何已检查异常
@SneakyThrows
public SQLStatement fill(final Collection<SQLSegment> sqlSegments, final SQLStatementRule rule) {
SQLStatement result = rule.getSqlStatementClass().newInstance();
//遍历SQL片段,从SQL解析规则注册表中找到相应SQL填充器
for (SQLSegment each : sqlSegments) {
Optional<SQLStatementFiller> filler = parsingRuleRegistry.findSQLStatementFiller(each.getClass());
if (filler.isPresent()) {
//填充解析的结果中
filler.get().fill(each, result, sql, shardingRule, shardingTableMetaData);
}
}
return result;
}
}
SQL优化器
@RequiredArgsConstructor
public final class SQLStatementOptimizerEngine {
private final ShardingTableMetaData shardingTableMetaData;
/**
* Optimize SQL statement.
*
* @param rule SQL statement rule
* @param sqlStatement SQL statement
*/
public void optimize(final SQLStatementRule rule, final SQLStatement sqlStatement) {
Optional<SQLStatementOptimizer> optimizer = rule.getOptimizer();
if (optimizer.isPresent()) {
optimizer.get().optimize(sqlStatement, shardingTableMetaData);
}
}
}