说明
ShardingSphere-5.0.0-beta使用ANTLR 作为 SQL 解析引擎的生成器。ShardingSphere使用sql解析引擎主要是为了提取抽象语法树,为了后面的路由和改写提供基础。
抽象语法树
解析过程分为词法解析和语法解析。 词法解析器用于将 SQL 拆解为不可再分的原子符号,称为 Token。并根据不同数据库方言所提供的字典,将其归类为关键字,表达式,字面量和操作符。 再使用语法解析器将词法解析器的输出转换为抽象语法树。
例如,以下 SQL:
SELECT id, name FROM t_user WHERE status = 'ACTIVE' AND age > 18
解析之后的为抽象语法树见下图。
为了便于理解,抽象语法树中的关键字的 Token 用绿色表示,变量的 Token 用红色表示,灰色表示需要进一步拆分。
最后,通过 visitor
对抽象语法树遍历构造域模型,通过域模型(SQLStatement
)去提炼分片所需的上下文,并标记有可能需要改写的位置。 供分片使用的解析上下文包含查询选择项(Select Items)、表信息(Table)、分片条件(Sharding Condition)、自增主键信息(Auto increment Primary Key)、排序信息(Order By)、分组信息(Group By)以及分页信息(Limit、Rownum、Top)。 SQL 的一次解析过程是不可逆的,一个个 Token 按 SQL 原本的顺序依次进行解析,性能很高。 考虑到各种数据库 SQL 方言的异同,在解析模块提供了各类数据库的 SQL 方言字典。
DatabaseTypedSQLParserFacade
SPI 名称 | 详细说明 |
---|---|
DatabaseTypedSQLParserFacade | 配置用于SQL解析的词法分析器和语法分析器入口 |
Implementation Class | Description |
---|---|
MySQLParserFacade | 基于 MySQL 的 SQL 解析器入口 |
PostgreSQLParserFacade | 基于 PostgreSQL 的SQL 解析器入口 |
SQLServerParserFacade | 基于 SQLServer 的SQL 解析器入口 |
OracleParserFacade | 基于 Oracle 的SQL 解析器入口 |
SQL92ParserFacade | 基于 SQL92 的SQL 解析器入口 |
SQLVisitorFacade
SPI 名称 | 详细说明 |
---|---|
SQLVisitorFacade | SQL 语法树访问器入口 |
Implementation Class | Description |
---|---|
MySQLStatementSQLVisitorFacade | 基于 MySQL 的提取 SQL 语句的语法树访问器 |
PostgreSQLStatementSQLVisitorFacade | 基于 PostgreSQL 的提取 SQL 语句的语法树访问器 |
SQLServerStatementSQLVisitorFacade | 基于 SQLServer 的提取 SQL 语句的语法树访问器 |
OracleStatementSQLVisitorFacade | 基于 Oracle 的提取 SQL 语句的语法树访问器 |
SQL92StatementSQLVisitorFacade | 基于 SQL92 的提取 SQL 语句的语法树访问器 |
@Override
public void createTableIfNotExists() throws SQLException {
String sql = "CREATE TABLE IF NOT EXISTS t_order (order_id BIGINT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, address_id BIGINT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_id))";
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
statement.executeUpdate(sql);
}
}
通过jdbc执行开始parse引擎的探索,在我们的sql语句执行的过程中,是shardingsphere将我们用于逻辑表(单库单表)执行的sql,改写为对实际表(可能多库多表,取决于分片配置)的多条sql,最后执行的研究。
@Override
public int executeUpdate(final String sql) throws SQLException {
try {
executionContext = createExecutionContext(sql);
if (metaDataContexts.getMetaData(connection.getSchemaName()).getRuleMetaData().getRules().stream().anyMatch(each -> each instanceof RawExecutionRule)) {
return accumulate(rawExecutor.execute(createRawExecutionContext(), executionContext.getLogicSQL(), new RawSQLExecutorCallback()));
}
ExecutionGroupContext<JDBCExecutionUnit> executionGroupContext = createExecutionContext();
cacheStatements(executionGroupContext.getInputGroups());
return executeUpdate(executionGroupContext,
(actualSQL, statement) -> statement.executeUpdate(actualSQL), executionContext.getSqlStatementContext(), executionContext.getRouteContext().getRouteUnits());
} finally {
currentResultSet = null;
}
}
private ExecutionContext createExecutionContext(final String sql) throws SQLException {
clearStatements();
//构建LogicSQL
LogicSQL logicSQL = createLogicSQL(sql);
//检查
SQLCheckEngine.check(logicSQL.getSqlStatementContext().getSqlStatement(), logicSQL.getParameters(),
metaDataContexts.getMetaData(connection.getSchemaName()).getRuleMetaData().getRules(), connection.getSchemaName(), metaDataContexts.getMetaDataMap(), null);
//执行
return kernelProcessor.generateExecutionContext(logicSQL, metaDataContexts.getMetaData(connection.getSchemaName()), metaDataContexts.getProps());
}
private LogicSQL createLogicSQL(final String sql) {
//解析引擎
ShardingSphereSQLParserEngine sqlParserEngine = new ShardingSphereSQLParserEngine(
DatabaseTypeRegistry.getTrunkDatabaseTypeName(metaDataContexts.getMetaData(connection.getSchemaName()).getResource().getDatabaseType()));
//通过解析获取SQLStatement
SQLStatement sqlStatement = sqlParserEngine.parse(sql, false);
SQLStatementContext<?> sqlStatementContext = SQLStatementContextFactory.newInstance(metaDataContexts.getMetaDataMap(), Collections.emptyList(), sqlStatement,
connection.getSchemaName());
return new LogicSQL(sqlStatementContext, sql, Collections.emptyList());
}
public SQLStatement parse(final String sql, final boolean useCache) {
try {
return parse0(sql, useCache);
// CHECKSTYLE:OFF
// TODO check whether throw SQLParsingException only
} catch (final Exception ex) {
// CHECKSTYLE:ON
throw ex;
}
}
private SQLStatement parse0(final String sql, final boolean useCache) {
try {
return sqlStatementParserEngine.parse(sql, useCache);
} catch (final SQLParsingException | ParseCancellationException originalEx) {
try {
return distSQLStatementParserEngine.parse(sql);
} catch (final SQLParsingException ignored) {
throw originalEx;
}
}
}
public SQLStatement parse(final String sql, final boolean useCache) {
return useCache ? sqlStatementCache.getUnchecked(sql) : sqlStatementParserExecutor.parse(sql);
}
public SQLStatement parse(final String sql) {
return visitorEngine.visit(parserEngine.parse(sql, false));
}
public ParseTree parse(final String sql) {
ParseASTNode result = twoPhaseParse(sql);
if (result.getRootNode() instanceof ErrorNode) {
throw new SQLParsingException("Unsupported SQL of `%s`", sql);
}
return result.getRootNode();
}
private ParseASTNode twoPhaseParse(final String sql) {
//通过databaseType获取DatabaseTypedSQLParserFacade
DatabaseTypedSQLParserFacade sqlParserFacade = DatabaseTypedSQLParserFacadeRegistry.getFacade(databaseType);
SQLParser sqlParser = SQLParserFactory.newInstance(sql, sqlParserFacade.getLexerClass(), sqlParserFacade.getParserClass());
try {
((Parser) sqlParser).getInterpreter().setPredictionMode(PredictionMode.SLL);
//执行
return (ParseASTNode) sqlParser.parse();
} catch (final ParseCancellationException ex) {
((Parser) sqlParser).reset();
((Parser) sqlParser).getInterpreter().setPredictionMode(PredictionMode.LL);
try {
return (ParseASTNode) sqlParser.parse();
} catch (final ParseCancellationException e) {
throw new SQLParsingException("You have an error in your SQL syntax");
}
}
}
public static SQLParser newInstance(final String sql, final Class<? extends SQLLexer> lexerClass, final Class<? extends SQLParser> parserClass) {
return createSQLParser(createTokenStream(sql, lexerClass), parserClass);
}
@SneakyThrows(ReflectiveOperationException.class)
private static TokenStream createTokenStream(final String sql, final Class<? extends SQLLexer> lexerClass) {
Lexer lexer = (Lexer) lexerClass.getConstructor(CharStream.class).newInstance(getSQLCharStream(sql));
return new CommonTokenStream(lexer);
}
@Override
public ASTNode parse() {
// 开始构建语法树
return new ParseASTNode(execute());
}
public final ExecuteContext execute() throws RecognitionException {
ExecuteContext _localctx = new ExecuteContext(_ctx, getState());
enterRule(_localctx, 0, RULE_execute);
try {
setState(1254);
_errHandler.sync(this);
switch (_input.LA(1)) {
case LP_:
case ALTER:
case ANALYZE:
case BEGIN:
case BINLOG:
case CACHE:
case CALL:
case CHANGE:
case CHECK:
case CHECKSUM:
case CLONE:
public void enterRule(ParserRuleContext localctx, int state, int ruleIndex) {
this.setState(state);
this._ctx = localctx;
this._ctx.start = this._input.LT(1);
if (this._buildParseTrees) {
this.addContextToParseTree();
}
if (this._parseListeners != null) {
this.triggerEnterRuleEvent();
}
}
public <T> T visit(final ParseTree parseTree) {
ParseTreeVisitor<T> visitor = SQLVisitorFactory.newInstance(databaseType, visitorType, SQLVisitorRule.valueOf(parseTree.getClass()), props);
return parseTree.accept(visitor);
}
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
if ( visitor instanceof MySQLStatementVisitor ) return ((MySQLStatementVisitor<? extends T>)visitor).visitCreateTable(this);
else return visitor.visitChildren(this);
}
public ExecutionContext generateExecutionContext(final LogicSQL logicSQL, final ShardingSphereMetaData metaData, final ConfigurationProperties props) {
//路由
RouteContext routeContext = route(logicSQL, metaData, props);
//重写
SQLRewriteResult rewriteResult = rewrite(logicSQL, metaData, props, routeContext);
//执行
ExecutionContext result = createExecutionContext(logicSQL, metaData, routeContext, rewriteResult);
//日志
logSQL(logicSQL, props, result);
return result;
}