sharding-sephere源码解析之sql解析
这里我们以org.apache.shardingsphere.shardingjdbc.jdbc.core.statement.ShardingPreparedStatement为起点。
ShardingPreparedStatement构造方法入参
结构图:
源码:
sql解析和路由部分主要在属性 private final PreparedQueryShardingEngine shardingEngine;中,这里我们主要分析下这个类,关于其他属性,之后会有专门篇幅来讲。
PreparedQueryShardingEngine
类结构图:
- 属性PreparedStatementRoutingEngine routingEngine:路由的引擎,是最终执行解析和路由的关键部分。
- route方法,是执行解析和路由并返回路由结果的方法。
- shard方法,是根据分片信息对sql获取解析和路由信息。
下面我们来看一看。
PreparedStatementRoutingEngine
看下源码:
1. `public final class PreparedStatementRoutingEngine {`
2. `/**`
3. `* 逻辑sql`
4. `*/`
5. `private final String logicSQL;`
6. `/**`
7. `* 分片路由`
8. `*/`
9. `private final ShardingRouter shardingRouter;`
10. `/**`
11. `* 主从路由`
12. `*/`
13. `private final ShardingMasterSlaveRouter masterSlaveRouter;`
14.
15. `/**`
16. `* sql statement`
17. `*/`
18. `private SQLStatement sqlStatement;`
19.
20. `public PreparedStatementRoutingEngine(final String logicSQL, final ShardingRule shardingRule,`
21. `final ShardingMetaData shardingMetaData, final DatabaseType databaseType, final ParsingResultCache parsingResultCache) {`
22. `this.logicSQL = logicSQL;`
23. `//获取分片路由`
24. `shardingRouter = ShardingRouterFactory.newInstance(shardingRule, shardingMetaData, databaseType, parsingResultCache);`
25. `//获取主从路由`
26. `masterSlaveRouter = new ShardingMasterSlaveRouter(shardingRule.getMasterSlaveRules());`
27. `}`
28.
29. `/**`
30. `* SQL route.`
31. `*`
32. `* <p>First routing time will parse SQL, after second time will reuse first parsed result.</p>`
33. `*`
34. `* @param parameters parameters of SQL placeholder`
35. `* @return route result`
36. `*/`
37. `public SQLRouteResult route(final List<Object> parameters) {`
38. `if (null == sqlStatement) {`
39. `sqlStatement = shardingRouter.parse(logicSQL, true);`
40. `}`
41. `//这里如果masterSlaveRouter的masterSlaveRules属性集合为空,则会直接返回shardingRouter.route(sqlStatement, parameters)的结果`
42. `return masterSlaveRouter.route(shardingRouter.route(sqlStatement, parameters));`
43. `}`
44. `}`
可以看到,这个类里面主要起作用的是两类router,ShardingRouter和ShardingMasterSlaveRouter。我们从类继承图来分别看下他们的实现。ShardingRouter:
ShardingMasterSlaveRouter:
从PreparedStatementRoutingEngine的route方法可以看出:
- 当开始进来时,没有缓存,sqlStatement为null,会进入shardingRouter的parse方法。
- shardingRouter.route(sqlStatement, parameters)方法的入参sqlStatement是sharding.parse之后得到的sqlStatement。
- 在使用时masterSlaveRouter的route方法的入参是shardingRouter.route(sqlStatement, parameters)的结果。
这里对于其他类型的路由部分我们不作过多解释,主要关注下ShardingRouter的一个实现ParsingSQLRouter中的解析部分,也就是它的parse方法。
org.apache.shardingsphere.core.route.router.sharding.ParsingSQLRouter#parse
1. `public final class ParsingSQLRouter implements ShardingRouter {`
2. `/**`
3. `* 分片规则`
4. `*/`
5. `private final ShardingRule shardingRule;`
6.
7. `/**`
8. `* 分片元数据信息`
9. `*/`
10. `private final ShardingMetaData shardingMetaData;`
11.
12. `/**`
13. `* 数据库类型`
14. `*/`
15. `private final DatabaseType databaseType;`
16.
17. `/**`
18. `* 解析结果的缓存`
19. `*/`
20. `private final ParsingResultCache parsingResultCache;`
21.
22. `private final List<Comparable<?>> generatedValues = new LinkedList<>();`
23.
24. `/**`
25. `* 一个用于spi拓展的钩子`
26. `*/`
27. `private final ParsingHook parsingHook = new SPIParsingHook();`
28.
29. `@Override`
30. `public SQLStatement parse(final String logicSQL, final boolean useCache) {`
31. `parsingHook.start(logicSQL);`
32. `try {`
33. `//parse方法调用的是ShardingSQLParseEntry的parse方法`
34. `SQLStatement result = new ShardingSQLParseEntry(databaseType, shardingMetaData.getTable(), parsingResultCache).parse(logicSQL, useCache);`
35. `parsingHook.finishSuccess(result, shardingMetaData.getTable());`
36. `return result;`
37. `// CHECKSTYLE:OFF`
38. `} catch (final Exception ex) {`
39. `// CHECKSTYLE:ON`
40. `parsingHook.finishFailure(ex);`
41. `throw ex;`
42. `}`
43. `}`
44.
45. `@Override`
46. `public SQLRouteResult route(final SQLStatement sqlStatement, final List<Object> parameters) {`
47. `....`
48. `}`
49. `}`
可以看到,这里的parse方法实际调用的是ShardingSQLParseEntry的parse方法。我们继续来看ShardingSQLParseEntry的parse方法。
ShardingSQLParseEntry
它的代码:
1. `public final class ShardingSQLParseEntry extends SQLParseEntry {`
2. `/**`
3. `* 数据库类型`
4. `*/`
5. `private final DatabaseType databaseType;`
6. `/**`
7. `* 分表元数据信息 这里是解析sql,所以需要分表信息`
8. `*/`
9. `private final ShardingTableMetaData shardingTableMetaData;`
10.
11. `public ShardingSQLParseEntry(final DatabaseType databaseType, final ShardingTableMetaData shardingTableMetaData, final ParsingResultCache parsingResultCache) {`
12. `super(parsingResultCache);`
13. `this.databaseType = databaseType;`
14. `this.shardingTableMetaData = shardingTableMetaData;`
15. `}`
16.
17. `@Override`
18. `protected SQLParseEngine getSQLParseEngine(final String sql) {`
19. `//新建SQLParseEngine 也就是sql解析引擎`
20. `return new SQLParseEngine(ShardingParseRuleRegistry.getInstance(), databaseType, sql, shardingTableMetaData);`
21. `}`
22. `}`
它父类的源码:
1. `@RequiredArgsConstructor`
2. `public abstract class SQLParseEntry {`
3.
4. `/**`
5. `* 缓存解析结果的`
6. `*/`
7. `private final ParsingResultCache parsingResultCache;`
8.
9. `/**`
10. `* Parse SQL.`
11. `*`
12. `* @param sql SQL`
13. `* @param useCache use cache or not`
14. `* @return SQL statement`
15. `*/`
16. `public final SQLStatement parse(final String sql, final boolean useCache) {`
17. `Optional<SQLStatement> cachedSQLStatement = getSQLStatementFromCache(sql, useCache);`
18. `if (cachedSQLStatement.isPresent()) {`
19. `return cachedSQLStatement.get();`
20. `}`
21. `//这里调用的是子类中实现的getSQLParseEngine方法获取sql解析引擎`
22. `SQLStatement result = getSQLParseEngine(sql).parse();`
23. `if (useCache) {`
24. `parsingResultCache.put(sql, result);`
25. `}`
26. `return result;`
27. `}`
28.
29. `private Optional<SQLStatement> getSQLStatementFromCache(final String sql, final boolean useCache) {`
30. `//从缓存中获取,并返回一个Optional对象,避免NPE`
31. `return useCache ? Optional.fromNullable(parsingResultCache.getSQLStatement(sql)) : Optional.<SQLStatement>absent();`
32. `}`
33.
34. `protected abstract SQLParseEngine getSQLParseEngine(String sql);`
35. `}`
可以看出,在parse方法中调用的是SQLParseEngine的parse方法,我们接着往下看。
SQLParseEngine
源代码:
1. `public final class SQLParseEngine {`
2.
3. `/**`
4. `* sql解析引擎,真正的sql解析引擎`
5. `*/`
6. `private final SQLParserEngine parserEngine;`
7.
8. `/**`
9. `* sql片段提取引擎`
10. `*/`
11. `private final SQLSegmentsExtractorEngine extractorEngine;`
12.
13. `/**`
14. `* sql statement 过滤引擎`
15. `*/`
16. `private final SQLStatementFillerEngine fillerEngine;`
17.
18. `public SQLParseEngine(final ParseRuleRegistry parseRuleRegistry, final DatabaseType databaseType, final String sql, final ShardingTableMetaData shardingTableMetaData) {`
19. `DatabaseType trunkDatabaseType = DatabaseTypes.getTrunkDatabaseType(databaseType.getName());`
20. `parserEngine = new SQLParserEngine(parseRuleRegistry, trunkDatabaseType, sql);`
21. `extractorEngine = new SQLSegmentsExtractorEngine();`
22. `fillerEngine = new SQLStatementFillerEngine(parseRuleRegistry, trunkDatabaseType, sql, shardingTableMetaData);`
23. `}`
24.
25. `/**`
26. `* Parse SQL.`
27. `*`
28. `* @return SQL statement`
29. `*/`
30. `public SQLStatement parse() {`
31. `SQLAST ast = parserEngine.parse();`
32. `Collection<SQLSegment> sqlSegments = extractorEngine.extract(ast);`
33. `Map<ParserRuleContext, Integer> parameterMarkerIndexes = ast.getParameterMarkerIndexes();`
34. `return fillerEngine.fill(sqlSegments, parameterMarkerIndexes.size(), ast.getSqlStatementRule());`
35. `}`
36. `}`
- SQLParserEngine parserEngine sql真正的解析引擎。
- SQLSegmentsExtractorEngine extractorEngine 用来指取SQLAST语法书中的sql片段。
- SQLStatementFillerEngine fillerEngine 是用来生成最后的SQLStatement的。
SQLParserEngine
源代码:
1. `@RequiredArgsConstructor`
2. `public final class SQLParserEngine {`
3.
4. `private final ParseRuleRegistry parseRuleRegistry;`
5.
6. `private final DatabaseType databaseType;`
7.
8. `private final String sql;`
9.
10. `/**`
11. `* Parse SQL to abstract syntax tree.`
12. `*`
13. `* @return abstract syntax tree of SQL`
14. `*/`
15. `public SQLAST parse() {`
16. `//1. 生成语法树`
17. `ParseTree parseTree = SQLParserFactory.newInstance(databaseType, sql).execute().getChild(0);`
18. `if (parseTree instanceof ErrorNode) {`
19. `throw new SQLParsingException(String.format("Unsupported SQL of `%s`", sql));`
20. `}`
21. `//2. sql statement的规则`
22. `SQLStatementRule sqlStatementRule = parseRuleRegistry.getSQLStatementRule(databaseType, parseTree.getClass().getSimpleName());`
23. `if (null == sqlStatementRule) {`
24. `throw new SQLParsingException(String.format("Unsupported SQL of `%s`", sql));`
25. `}`
26. `//3. sql的ast 语法树`
27. `return new SQLAST((ParserRuleContext) parseTree, getParameterMarkerIndexes((ParserRuleContext) parseTree), sqlStatementRule);`
28. `}`
29.
30. `private Map<ParserRuleContext, Integer> getParameterMarkerIndexes(final ParserRuleContext rootNode) {`
31. `//获取到树节点下所有子节点`
32. `Collection<ParserRuleContext> placeholderNodes = ExtractorUtils.getAllDescendantNodes(rootNode, RuleName.PARAMETER_MARKER);`
33. `Map<ParserRuleContext, Integer> result = new HashMap<>(placeholderNodes.size(), 1);`
34. `int index = 0;`
35. `for (ParserRuleContext each : placeholderNodes) {`
36. `//将节点信息放入result中`
37. `result.put(each, index++);`
38. `}`
39. `return result;`
40. `}`
41. `}`
上面的过程说明如下:
- 生成语法树
1. `ParseTree
parseTree
=
SQLParserFactory
.
newInstance
(
databaseType
,
sql
).
execute
().
getChild
(
0
);`
SQLParserFactory.newInstance:
1. `/**`
2. `* New instance of SQL parser.`
3. `*`
4. `* @param databaseType database type`
5. `* @param sql SQL`
6. `* @return SQL parser`
7. `*/`
8. `public static SQLParser newInstance(final DatabaseType databaseType, final String sql) {`
9. `//根据数据库类型在SPI拓展里面获取SQLParserEntry实例`
10. `for (SQLParserEntry each : NewInstanceServiceLoader.newServiceInstances(SQLParserEntry.class)) {`
11. `if (DatabaseTypes.getActualDatabaseType(each.getDatabaseType()) == databaseType) {`
12. `return createSQLParser(sql, each);`
13. `}`
14. `}`
15. `throw new UnsupportedOperationException(String.format("Cannot support database type '%s'", databaseType));`
16. `}`
- NewInstanceServiceLoader是一个sharding-jdbc的spi拓展点加载器,用于加载SQLParserEntry的实现,可以看下sharding-core-parse-mysql这个module下的:
sharding-core-parse-* 其他类型的数据库中也是类似。
- 这里使用的SQLParserEntry是:
- createSQLParser(sql, each)方法:
1. `@SneakyThrows`
2. `private static SQLParser createSQLParser(final String sql, final SQLParserEntry parserEntry) {`
3. `Lexer lexer = parserEntry.getLexerClass().getConstructor(CharStream.class).newInstance(CharStreams.fromString(sql));`
4. `return parserEntry.getParserClass().getConstructor(TokenStream.class).newInstance(new CommonTokenStream(lexer));`
5. `}`
parserEntry.getParserClass()获取到的是MySQLParser,我们看下MySQLParser的实现:
1. `public final class MySQLParser extends MySQLStatementParser implements SQLParser {`
2.
3. `public MySQLParser(final TokenStream input) {`
4. `super(input);`
5. `}`
6. `}`
这里需要关注一下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):
- sql statement的规则 根据ParseTree的结果生成SQLStatementRule:
org.apache.shardingsphere.core.parse.rule.registry.ParseRuleRegistry#getSQLStatementRule:
1. `/**`
2. `* Get SQL statement rule.`
3. `*`
4. `* @param databaseType database type`
5. `* @param contextClassName context class name`
6. `* @return SQL statement rule`
7. `*/`
8. `public SQLStatementRule getSQLStatementRule(final DatabaseType databaseType, final String contextClassName) {`
9. `return sqlStatementRuleDefinitions.get(databaseType).getSQLStatementRule(contextClassName);`
10. `}`
这个sqlStatementRuleDefinitions是在org.apache.shardingsphere.core.parse.rule.registry.ParseRuleRegistry#initParseRuleDefinition中加载的:
1. `public ParseRuleRegistry() {`
2. `initParseRuleDefinition();`
3. `}`
4.
5. `private void initParseRuleDefinition() {`
6. `ExtractorRuleDefinitionEntity generalExtractorRuleEntity = extractorRuleLoader.load(RuleDefinitionFileConstant.getExtractorRuleDefinitionFile());`
7. `FillerRuleDefinitionEntity generalFillerRuleEntity = fillerRuleLoader.load(RuleDefinitionFileConstant.getFillerRuleDefinitionFile());`
8. `FillerRuleDefinitionEntity featureGeneralFillerRuleEntity = fillerRuleLoader.load(RuleDefinitionFileConstant.getFillerRuleDefinitionFile(getType()));`
9. `for (DatabaseType each : SQLParserFactory.getAddOnDatabaseTypes()) {`
10. `fillerRuleDefinitions.put(each, createFillerRuleDefinition(generalFillerRuleEntity, featureGeneralFillerRuleEntity, each));`
11. `sqlStatementRuleDefinitions.put(each, createSQLStatementRuleDefinition(generalExtractorRuleEntity, each));`
12. `}`
13. `}`
加载的内容为:
- sql的ast 语法树 SQLAST:
1. `@RequiredArgsConstructor`
2. `@Getter`
3. `public final class SQLAST {`
4.
5. `private final ParserRuleContext parserRuleContext;`
6.
7. `private final Map<ParserRuleContext, Integer> parameterMarkerIndexes;`
8.
9. `private final SQLStatementRule sqlStatementRule;`
10. `}`
可以看到SQLStatementRule的信息和parserRuleContext都是包括在SQLAST中的。
这时我们再回过头来看一下org.apache.shardingsphere.core.route.router.sharding.ParsingSQLRouter#parse:
1. `@Override`
2. `public SQLStatement parse(final String logicSQL, final boolean useCache) {`
3. `parsingHook.start(logicSQL);`
4. `try {`
5. `//parse方法调用的是ShardingSQLParseEntry的parse方法`
6. `SQLStatement result = new ShardingSQLParseEntry(databaseType, shardingMetaData.getTable(), parsingResultCache).parse(logicSQL, useCache);`
7. `parsingHook.finishSuccess(result, shardingMetaData.getTable());`
8. `return result;`
9. `// CHECKSTYLE:OFF`
10. `} catch (final Exception ex) {`
11. `// CHECKSTYLE:ON`
12. `parsingHook.finishFailure(ex);`
13. `throw ex;`
14. `}`
15. `}`
通过下面一系列的解析之后,这里就可以在ParsingSQLRouter的parse方法中获取到SQLStatement然后用于到sql的路由中去了。
