公众号:帽爹的技术轮子
Sharding主要利用ANTLR4来解析SQL,以mysql为例,分析源码前可以先了解以下三点:
- antlr4,如何编写
.g4
语法文件 - mysql 语法可以参考https://dev.mysql.com/doc/refman/8.0/en/sql-syntax-data-manipulation.html
- mysql g4文件编写可以参考https://github.com/antlr/grammars-v4/blob/master/mysql
源码分析
1.解析入口ParsingSQLRouter#parse
/**
* 解析sql
*
* @param logicSQL 逻辑sql
* @param useCache 是否缓存解析后的结果
* @return
*/
@Override
public SQLStatement parse(final String logicSQL, final boolean useCache) {
//解析前钩子,如:调用链etx
parsingHook.start(logicSQL);
try {
//解析SQL
SQLStatement result = new ShardingSQLParseEntry(databaseType, shardingMetaData.getTable(), parsingResultCache).parse(logicSQL, useCache);
//解析成功后钩子
parsingHook.finishSuccess(result, shardingMetaData.getTable());
return result;
// CHECKSTYLE:OFF
} catch (final Exception ex) {
// CHECKSTYLE:ON
//解析失败钩子
parsingHook.finishFailure(ex);
throw ex;
}
}
public final class ShardingSQLParseEntry extends SQLParseEntry {
private final DatabaseType databaseType;
private final ShardingTableMetaData shardingTableMetaData;
public ShardingSQLParseEntry(final DatabaseType databaseType, final ShardingTableMetaData shardingTableMetaData, final ParsingResultCache parsingResultCache) {
super(parsingResultCache);
this.databaseType = databaseType;
this.shardingTableMetaData = shardingTableMetaData;
}
/**
* 根据sql获取解析引擎封装对象
*/
@Override
protected SQLParseEngine getSQLParseEngine(final String sql) {
//参数1:单例,加载statement、提取、过滤配置文件
//参数2:数据库类型
//参数3:需要解析sql
//参数4:分片表元数据
return new SQLParseEngine(ShardingParseRuleRegistry.getInstance(), databaseType, sql, shardingTableMetaData);
}
}
2.ShardingParseRuleRegistry.getInstance()->ParseRuleRegistry#initParseRuleDefinition加载statement、提取、过滤配置文件
private void initParseRuleDefinition() {
//利用JAXB加载META-INF/parsing-rule-definition/extractor-rule-definition.xml配置文件
ExtractorRuleDefinitionEntity generalExtractorRuleEntity = extractorRuleLoader.load(RuleDefinitionFileConstant.getExtractorRuleDefinitionFile());
//利用JAXB加载下META-INF/parsing-rule-definition/filler-rule-definition.xml配置文件
FillerRuleDefinitionEntity generalFillerRuleEntity = fillerRuleLoader.load(RuleDefinitionFileConstant.getFillerRuleDefinitionFile());
//加对应类型(sharding、masterslave、encrypt)配置文件
//META-INF/parsing-rule-definition/sharding/filler-rule-definition.xml
FillerRuleDefinitionEntity featureGeneralFillerRuleEntity = fillerRuleLoader.load(RuleDefinitionFileConstant.getFillerRuleDefinitionFile(getType()));
//根据数据库类型加载对应的配置文件
for (DatabaseType each : SQLParserFactory.getAddOnDatabaseTypes()) {
//META-INF/parsing-rule-definition/sharding.mysql/filler-rule-definition.xml
//databaseType:rules<segment,filler>
fillerRuleDefinitions.put(each, createFillerRuleDefinition(generalFillerRuleEntity, featureGeneralFillerRuleEntity, each));
//META-INF/parsing-rule-definition/sharding.mysql/extractor-rule-definition.xml
//META-INF/parsing-rule-definition/sharding.mysql/sql-statement-rule-definition.xml
//databaseType:rules<xxxContext,SQLStatementRule>
sqlStatementRuleDefinitions.put(each, createSQLStatementRuleDefinition(generalExtractorRuleEntity, each));
}
}
private FillerRuleDefinition createFillerRuleDefinition(final FillerRuleDefinitionEntity generalFillerRuleEntity,
final FillerRuleDefinitionEntity featureGeneralFillerRuleEntity, final DatabaseType databaseType) {
return new FillerRuleDefinition(
generalFillerRuleEntity, featureGeneralFillerRuleEntity, fillerRuleLoader.load(RuleDefinitionFileConstant.getFillerRuleDefinitionFile(getType(), databaseType)));
}
private SQLStatementRuleDefinition createSQLStatementRuleDefinition(final ExtractorRuleDefinitionEntity generalExtractorRuleEntity, final DatabaseType databaseType) {
//将所有提取器封装到一起
//id:extractor
ExtractorRuleDefinition extractorRuleDefinition = new ExtractorRuleDefinition(
generalExtractorRuleEntity, extractorRuleLoader.load(RuleDefinitionFileConstant.getExtractorRuleDefinitionFile(getType(), databaseType)));
//sql-statement-rule-definition.xml
//Context:SQLStatementRule
//SQLStatementRule封装statement对应的提取器
return new SQLStatementRuleDefinition(statementRuleLoader.load(RuleDefinitionFileConstant.getSQLStatementRuleDefinitionFile(getType(), databaseType)), extractorRuleDefinition);
}
3.SQLParseEntry#parse,这里抽象SQLParseEntry,主要有不同入口(EncryptSQLParseEntry、MasterSlaveSQLParseEntry、ShardingSQLParseEntry)
@RequiredArgsConstructor
public abstract class SQLParseEntry {
private final ParsingResultCache parsingResultCache;
/**
* Parse SQL.
*
* @param sql SQL
* @param useCache use cache or not
* @return SQL statement
*/
public final SQLStatement parse(final String sql, final boolean useCache) {
//从缓存中获取解析后的SQLStatement
Optional<SQLStatement> cachedSQLStatement = getSQLStatementFromCache(sql, useCache);
if (cachedSQLStatement.isPresent()) {
return cachedSQLStatement.get();
}
//解析
SQLStatement result = getSQLParseEngine(sql).parse();
//cache
if (useCache) {
parsingResultCache.put(sql, result);
}
return result;
}
private Optional<SQLStatement> getSQLStatementFromCache(final String sql, final boolean useCache) {
return useCache ? Optional.fromNullable(parsingResultCache.getSQLStatement(sql)) : Optional.<SQLStatement>absent();
}
//根据子类ShardingSQLParseEntry的getSQLParseEngine获取SQLParseEngine
protected abstract SQLParseEngine getSQLParseEngine(String sql);
}
4.SQLParseEngine#parse,包含解析、提取、填充SQLStatement
public SQLParseEngine(final ParseRuleRegistry parseRuleRegistry, final DatabaseType databaseType, final String sql, final ShardingTableMetaData shardingTableMetaData) {
DatabaseType trunkDatabaseType = DatabaseTypes.getTrunkDatabaseType(databaseType.getName());
//sql解析引擎
parserEngine = new SQLParserEngine(parseRuleRegistry, trunkDatabaseType, sql);
//sql提取引擎
extractorEngine = new SQLSegmentsExtractorEngine();
//sql填充引擎
fillerEngine = new SQLStatementFillerEngine(parseRuleRegistry, trunkDatabaseType, sql, shardingTableMetaData);
}
/**
* Parse SQL.
*
* @return SQL statement
*/
public SQLStatement parse() {
//利用ANTLR4 解析sql
SQLAST ast = parserEngine.parse();
//提取ast中的token,封装成对应的segment,如TableSegment、IndexSegment
Collection<SQLSegment> sqlSegments = extractorEngine.extract(ast);
Map<ParserRuleContext, Integer> parameterMarkerIndexes = ast.getParameterMarkerIndexes();
//填充SQLStatement
return fillerEngine.fill(sqlSegments, parameterMarkerIndexes.size(), ast.getSqlStatementRule());
}
5.SQLParserEngine#parse,解析SQL,封装AST(Abstract Syntax Tree 抽象语法树)
public SQLAST parse() {
//SPI 利用ANTLR4解析获取SQLParser(MySQLParserEntry)执行,获取解析树
ParseTree parseTree = SQLParserFactory.newInstance(databaseType, sql).execute().getChild(0);
if (parseTree instanceof ErrorNode) {
throw new SQLParsingException(String.format("Unsupported SQL of `%s`", sql));
}
//获取配置文件中的StatementContext,比如CreateTableContext、SelectContext
SQLStatementRule sqlStatementRule = parseRuleRegistry.getSQLStatementRule(databaseType, parseTree.getClass().getSimpleName());
if (null == sqlStatementRule) {
throw new SQLParsingException(String.format("Unsupported SQL of `%s`", sql));
}
//封装ast(Abstract Syntax Tree 抽象语法树)
return new SQLAST((ParserRuleContext) parseTree, getParameterMarkerIndexes((ParserRuleContext) parseTree), sqlStatementRule);
}
/**
* 递归获取所有参数占位符
*
* @param rootNode 根节点
* @return
*/
private Map<ParserRuleContext, Integer> getParameterMarkerIndexes(final ParserRuleContext rootNode) {
Collection<ParserRuleContext> placeholderNodes = ExtractorUtils.getAllDescendantNodes(rootNode, RuleName.PARAMETER_MARKER);
Map<ParserRuleContext, Integer> result = new HashMap<>(placeholderNodes.size(), 1);
int index = 0;
for (ParserRuleContext each : placeholderNodes) {
result.put(each, index++);
}
return result;
}
6.使用SQLParserFactory#newInstance创建SQLParser
/**
* New instance of SQL parser.
*
* @param databaseType database type
* @param sql SQL
* @return SQL parser
*/
public static SQLParser newInstance(final DatabaseType databaseType, final String sql) {
//SPI load所有扩展
for (SQLParserEntry each : NewInstanceServiceLoader.newServiceInstances(SQLParserEntry.class)) {
//判断数据库类型
if (DatabaseTypes.getActualDatabaseType(each.getDatabaseType()) == databaseType) {
//解析sql
return createSQLParser(sql, each);
}
}
throw new UnsupportedOperationException(String.format("Cannot support database type '%s'", databaseType));
}
@SneakyThrows
private static SQLParser createSQLParser(final String sql, final SQLParserEntry parserEntry) {
//词法分析器
Lexer lexer = parserEntry.getLexerClass().getConstructor(CharStream.class).newInstance(CharStreams.fromString(sql));
//语法分析器
return parserEntry.getParserClass().getConstructor(TokenStream.class).newInstance(new CommonTokenStream(lexer));
}
7.以select为例,分析第四步的SQL解析、提取、填充过程
利用idea的antlr4插件,使用Sharding的mysql .g4
文件解析SQL;如图:
参考上图,使用sharding parse解析模块提取(extractor) ParserRuleContext对应的参数封装成Segment
8.SQLSegmentsExtractorEngine#extract,参考第七部图,根据SQLStatementRule->tableReferences, columns, selectItems, where, predicate, groupBy, orderBy, limit, subqueryPredicate对应的提取器,生成对应类型的Segment
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<>();
//遍历Context对应提取器,封装成对应对应类型的Segment,比如TableSegment、IndexSegment
//以SELECT i.* FROM t_order o, t_order_item i WHERE o.order_id = i.order_id and o.order_id = ?为例
//SelectContext->SQLStatementRule
//SQLStatementRule->tableReferences, columns, selectItems, where, predicate, groupBy, orderBy, limit, subqueryPredicate
//分析九个提取器
for (SQLSegmentExtractor each : ast.getSqlStatementRule().getExtractors()) {
//分两种类型
//1.单一树,直接提取单一RuleName下的token;参看sql解析后的语法树对比比较清晰
if (each instanceof OptionalSQLSegmentExtractor) {
Optional<? extends SQLSegment> sqlSegment = ((OptionalSQLSegmentExtractor) each).extract(ast.getParserRuleContext(), ast.getParameterMarkerIndexes());
if (sqlSegment.isPresent()) {
result.add(sqlSegment.get());
}
//2.分叉树,需遍历提取RuleName下的所有Token;参看sql解析后的语法树对比比较清晰
} else if (each instanceof CollectionSQLSegmentExtractor) {
result.addAll(((CollectionSQLSegmentExtractor) each).extract(ast.getParserRuleContext(), ast.getParameterMarkerIndexes()));
}
}
return result;
}
}
9.SQLStatementFillerEngine#fill,封装SQLStatement,填充Segment
@RequiredArgsConstructor
public final class SQLStatementFillerEngine {
private final ParseRuleRegistry parseRuleRegistry;
private final DatabaseType databaseType;
private final String sql;
private final ShardingTableMetaData shardingTableMetaData;
/**
* Fill SQL statement.
*
* @param sqlSegments SQL segments
* @param parameterMarkerCount parameter marker count
* @param rule SQL statement rule
* @return SQL statement
*/
@SneakyThrows
public SQLStatement fill(final Collection<SQLSegment> sqlSegments, final int parameterMarkerCount, final SQLStatementRule rule) {
//如SelectStatement
SQLStatement result = rule.getSqlStatementClass().newInstance();
//逻辑sql
result.setLogicSQL(sql);
//参数个数
result.setParametersCount(parameterMarkerCount);
//segment
result.getSQLSegments().addAll(sqlSegments);
//遍历填充对应类型的Segment
for (SQLSegment each : sqlSegments) {
//根据数据库类型、segment找到对应填充器,来填充对应的segment
//如:TableSegment->TableFiller
Optional<SQLSegmentFiller> filler = parseRuleRegistry.findSQLSegmentFiller(databaseType, each.getClass());
if (filler.isPresent()) {
doFill(each, result, filler.get());
}
}
return result;
}
@SuppressWarnings("unchecked")
private void doFill(final SQLSegment sqlSegment, final SQLStatement sqlStatement, final SQLSegmentFiller filler) {
//添加字段、字段约束、修改字段、字段命令,这四种填充器需要设置分片表元数据
//主要通过分片表元数据来填充对应的SQLStatement
if (filler instanceof ShardingTableMetaDataAware) {
((ShardingTableMetaDataAware) filler).setShardingTableMetaData(shardingTableMetaData);
}
//如:
//利用TableFill来填充SelectStatement#tables
filler.fill(sqlSegment, sqlStatement);
}
}
以上Sharding的SQL解析大概过程,解析ParserRuleContext提取封装对应的Segment,最后封装SQLStatement,并根据Segment对应的Filler来填充SQLStatement;具体如何提取、填充可以查看以下三个文件
extractor-rule-definition.xml
filler-rule-definition.xml
sql-statement-rule-definition.xml