ShardingSphere之解析引擎

780 阅读4分钟

说明

ShardingSphere-5.0.0-beta使用ANTLR 作为 SQL 解析引擎的生成器。ShardingSphere使用sql解析引擎主要是为了提取抽象语法树,为了后面的路由和改写提供基础。

抽象语法树

解析过程分为词法解析和语法解析。 词法解析器用于将 SQL 拆解为不可再分的原子符号,称为 Token。并根据不同数据库方言所提供的字典,将其归类为关键字,表达式,字面量和操作符。 再使用语法解析器将词法解析器的输出转换为抽象语法树。

例如,以下 SQL:

SELECT id, name FROM t_user WHERE status = 'ACTIVE' AND age > 18

解析之后的为抽象语法树见下图。

SQL抽象语法树

为了便于理解,抽象语法树中的关键字的 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 ClassDescription
MySQLParserFacade基于 MySQL 的 SQL 解析器入口
PostgreSQLParserFacade基于 PostgreSQL 的SQL 解析器入口
SQLServerParserFacade基于 SQLServer 的SQL 解析器入口
OracleParserFacade基于 Oracle 的SQL 解析器入口
SQL92ParserFacade基于 SQL92 的SQL 解析器入口

SQLVisitorFacade

SPI 名称详细说明
SQLVisitorFacadeSQL 语法树访问器入口
Implementation ClassDescription
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;
}