sharding-sephere源码解析之sql解析
这里我们以org.apache.shardingsphere.shardingjdbc.jdbc.core.statement.ShardingPreparedStatement为起点。
结构图:
源码:
sql解析和路由部分主要在属性 private final PreparedQueryShardingEngine shardingEngine;中,这里我们主要分析下这个类,关于其他属性,之后会有专门篇幅来讲。
类结构图:
下面我们来看一看。
看下源码:
public final class PreparedStatementRoutingEngine {
/**
* 逻辑sql
*/
private final String logicSQL;
/**
* 分片路由
*/
private final ShardingRouter shardingRouter;
/**
* 主从路由
*/
private final ShardingMasterSlaveRouter masterSlaveRouter;
/**
* sql statement
*/
private SQLStatement sqlStatement;
public PreparedStatementRoutingEngine(final String logicSQL, final ShardingRule shardingRule,
final ShardingMetaData shardingMetaData, final DatabaseType databaseType, final ParsingResultCache parsingResultCache) {
this.logicSQL = logicSQL;
//获取分片路由
shardingRouter = ShardingRouterFactory.newInstance(shardingRule, shardingMetaData, databaseType, parsingResultCache);
//获取主从路由
masterSlaveRouter = new ShardingMasterSlaveRouter(shardingRule.getMasterSlaveRules());
}
/**
* SQL route.
*
* <p>First routing time will parse SQL, after second time will reuse first parsed result.</p>
*
* @param parameters parameters of SQL placeholder
* @return route result
*/
public SQLRouteResult route(final List<Object> parameters) {
if (null == sqlStatement) {
sqlStatement = shardingRouter.parse(logicSQL, true);
}
//这里如果masterSlaveRouter的masterSlaveRules属性集合为空,则会直接返回shardingRouter.route(sqlStatement, parameters)的结果
return masterSlaveRouter.route(shardingRouter.route(sqlStatement, parameters));
}
}
可以看到,这个类里面主要起作用的是两类router,ShardingRouter和ShardingMasterSlaveRouter。我们从类继承图来分别看下他们的实现。ShardingRouter:
ShardingMasterSlaveRouter:
从PreparedStatementRoutingEngine的route方法可以看出:
这里对于其他类型的路由部分我们不作过多解释,主要关注下ShardingRouter的一个实现ParsingSQLRouter中的解析部分,也就是它的parse方法。
public final class ParsingSQLRouter implements ShardingRouter {
/**
* 分片规则
*/
private final ShardingRule shardingRule;
/**
* 分片元数据信息
*/
private final ShardingMetaData shardingMetaData;
/**
* 数据库类型
*/
private final DatabaseType databaseType;
/**
* 解析结果的缓存
*/
private final ParsingResultCache parsingResultCache;
private final List<Comparable<?>> generatedValues = new LinkedList<>();
/**
* 一个用于spi拓展的钩子
*/
private final ParsingHook parsingHook = new SPIParsingHook();
@Override
public SQLStatement parse(final String logicSQL, final boolean useCache) {
parsingHook.start(logicSQL);
try {
//parse方法调用的是ShardingSQLParseEntry的parse方法
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;
}
}
@Override
public SQLRouteResult route(final SQLStatement sqlStatement, final List<Object> parameters) {
....
}
}
可以看到,这里的parse方法实际调用的是ShardingSQLParseEntry的parse方法。我们继续来看ShardingSQLParseEntry的parse方法。
它的代码:
public final class ShardingSQLParseEntry extends SQLParseEntry {
/**
* 数据库类型
*/
private final DatabaseType databaseType;
/**
* 分表元数据信息 这里是解析sql,所以需要分表信息
*/
private final ShardingTableMetaData shardingTableMetaData;
public ShardingSQLParseEntry(final DatabaseType databaseType, final ShardingTableMetaData shardingTableMetaData, final ParsingResultCache parsingResultCache) {
super(parsingResultCache);
this.databaseType = databaseType;
this.shardingTableMetaData = shardingTableMetaData;
}
@Override
protected SQLParseEngine getSQLParseEngine(final String sql) {
//新建SQLParseEngine 也就是sql解析引擎
return new SQLParseEngine(ShardingParseRuleRegistry.getInstance(), databaseType, sql, shardingTableMetaData);
}
}
它父类的源码:
@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) {
Optional<SQLStatement> cachedSQLStatement = getSQLStatementFromCache(sql, useCache);
if (cachedSQLStatement.isPresent()) {
return cachedSQLStatement.get();
}
//这里调用的是子类中实现的getSQLParseEngine方法获取sql解析引擎
SQLStatement result = getSQLParseEngine(sql).parse();
if (useCache) {
parsingResultCache.put(sql, result);
}
return result;
}
private Optional<SQLStatement> getSQLStatementFromCache(final String sql, final boolean useCache) {
//从缓存中获取,并返回一个Optional对象,避免NPE
return useCache ? Optional.fromNullable(parsingResultCache.getSQLStatement(sql)) : Optional.<SQLStatement>absent();
}
protected abstract SQLParseEngine getSQLParseEngine(String sql);
}
可以看出,在parse方法中调用的是SQLParseEngine的parse方法,我们接着往下看。
源代码:
public final class SQLParseEngine {
/**
* sql解析引擎,真正的sql解析引擎
*/
private final SQLParserEngine parserEngine;
/**
* sql片段提取引擎
*/
private final SQLSegmentsExtractorEngine extractorEngine;
/**
* sql statement 过滤引擎
*/
private final SQLStatementFillerEngine fillerEngine;
public SQLParseEngine(final ParseRuleRegistry parseRuleRegistry, final DatabaseType databaseType, final String sql, final ShardingTableMetaData shardingTableMetaData) {
DatabaseType trunkDatabaseType = DatabaseTypes.getTrunkDatabaseType(databaseType.getName());
parserEngine = new SQLParserEngine(parseRuleRegistry, trunkDatabaseType, sql);
extractorEngine = new SQLSegmentsExtractorEngine();
fillerEngine = new SQLStatementFillerEngine(parseRuleRegistry, trunkDatabaseType, sql, shardingTableMetaData);
}
/**
* Parse SQL.
*
* @return SQL statement
*/
public SQLStatement parse() {
SQLAST ast = parserEngine.parse();
Collection<SQLSegment> sqlSegments = extractorEngine.extract(ast);
Map<ParserRuleContext, Integer> parameterMarkerIndexes = ast.getParameterMarkerIndexes();
return fillerEngine.fill(sqlSegments, parameterMarkerIndexes.size(), ast.getSqlStatementRule());
}
}
源代码:
@RequiredArgsConstructor
public final class SQLParserEngine {
private final ParseRuleRegistry parseRuleRegistry;
private final DatabaseType databaseType;
private final String sql;
/**
* Parse SQL to abstract syntax tree.
*
* @return abstract syntax tree of SQL
*/
public SQLAST parse() {
//1. 生成语法树
ParseTree parseTree = SQLParserFactory.newInstance(databaseType, sql).execute().getChild(0);
if (parseTree instanceof ErrorNode) {
throw new SQLParsingException(String.format("Unsupported SQL of `%s`", sql));
}
//2. sql statement的规则
SQLStatementRule sqlStatementRule = parseRuleRegistry.getSQLStatementRule(databaseType, parseTree.getClass().getSimpleName());
if (null == sqlStatementRule) {
throw new SQLParsingException(String.format("Unsupported SQL of `%s`", sql));
}
//3. sql的ast 语法树
return new SQLAST((ParserRuleContext) parseTree, getParameterMarkerIndexes((ParserRuleContext) parseTree), sqlStatementRule);
}
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中
result.put(each, index++);
}
return result;
}
}
上面的过程说明如下:
ParseTree parseTree = SQLParserFactory.newInstance(databaseType, sql).execute().getChild(0);
SQLParserFactory.newInstance:
/**
* 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拓展里面获取SQLParserEntry实例
for (SQLParserEntry each : NewInstanceServiceLoader.newServiceInstances(SQLParserEntry.class)) {
if (DatabaseTypes.getActualDatabaseType(each.getDatabaseType()) == databaseType) {
return createSQLParser(sql, each);
}
}
throw new UnsupportedOperationException(String.format("Cannot support database type '%s'", databaseType));
}
sharding-core-parse-* 其他类型的数据库中也是类似。
@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));
}
parserEntry.getParserClass()获取到的是MySQLParser,我们看下MySQLParser的实现:
public final class MySQLParser extends MySQLStatementParser implements SQLParser {
public MySQLParser(final TokenStream input) {
super(input);
}
}
这里需要关注一下MySQLStatementParser,它是通过antlr4自动生成的,仍然以mysql为例,看sharding-core-parse-mysql这个module的配置。pom:
pom中对应的lib中的信息和要生成代码的g4文件的信息:
最终生成的代码文件:
所以这里获取到的parser实际上是antlr通过自己配置的语法解析g4文件来生成的。
MySQLStatementParser.execute:
这个方法返回的是ExecuteContext对象。
ExecuteContext.getChild(0)获取第一个子节点,获取解析树,实际调用的是org.antlr.v4.runtime.ParserRuleContext#getChild(int):
org.apache.shardingsphere.core.parse.rule.registry.ParseRuleRegistry#getSQLStatementRule:
/**
* Get SQL statement rule.
*
* @param databaseType database type
* @param contextClassName context class name
* @return SQL statement rule
*/
public SQLStatementRule getSQLStatementRule(final DatabaseType databaseType, final String contextClassName) {
return sqlStatementRuleDefinitions.get(databaseType).getSQLStatementRule(contextClassName);
}
这个sqlStatementRuleDefinitions是在org.apache.shardingsphere.core.parse.rule.registry.ParseRuleRegistry#initParseRuleDefinition中加载的:
public ParseRuleRegistry() {
initParseRuleDefinition();
}
private void initParseRuleDefinition() {
ExtractorRuleDefinitionEntity generalExtractorRuleEntity = extractorRuleLoader.load(RuleDefinitionFileConstant.getExtractorRuleDefinitionFile());
FillerRuleDefinitionEntity generalFillerRuleEntity = fillerRuleLoader.load(RuleDefinitionFileConstant.getFillerRuleDefinitionFile());
FillerRuleDefinitionEntity featureGeneralFillerRuleEntity = fillerRuleLoader.load(RuleDefinitionFileConstant.getFillerRuleDefinitionFile(getType()));
for (DatabaseType each : SQLParserFactory.getAddOnDatabaseTypes()) {
fillerRuleDefinitions.put(each, createFillerRuleDefinition(generalFillerRuleEntity, featureGeneralFillerRuleEntity, each));
sqlStatementRuleDefinitions.put(each, createSQLStatementRuleDefinition(generalExtractorRuleEntity, each));
}
}
加载的内容为:
@RequiredArgsConstructor
@Getter
public final class SQLAST {
private final ParserRuleContext parserRuleContext;
private final Map<ParserRuleContext, Integer> parameterMarkerIndexes;
private final SQLStatementRule sqlStatementRule;
}
可以看到SQLStatementRule的信息和parserRuleContext都是包括在SQLAST中的。
这时我们再回过头来看一下org.apache.shardingsphere.core.route.router.sharding.ParsingSQLRouter#parse:
@Override
public SQLStatement parse(final String logicSQL, final boolean useCache) {
parsingHook.start(logicSQL);
try {
//parse方法调用的是ShardingSQLParseEntry的parse方法
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;
}
}
通过下面一系列的解析之后,这里就可以在ParsingSQLRouter的parse方法中获取到SQLStatement然后用于到sql的路由中去了。