溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

sharding-jdbc的ANTLR4 SQL用法實(shí)例

發(fā)布時(shí)間:2021-06-22 17:04:17 來源:億速云 閱讀:675 作者:chen 欄目:大數(shù)據(jù)

這篇文章主要介紹“sharding-jdbc的ANTLR4 SQL用法實(shí)例”,在日常操作中,相信很多人在sharding-jdbc的ANTLR4 SQL用法實(shí)例問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”sharding-jdbc的ANTLR4 SQL用法實(shí)例”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!

Sharding主要利用ANTLR4來解析SQL,以mysql為例,分析源碼前可以先了解以下三點(diǎn):

  • 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 是否緩存解析后的結(jié)果
     * @return
     */
    @Override
    public SQLStatement parse(final String logicSQL, final boolean useCache) {
        //解析前鉤子,如:調(diào)用鏈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;
    }

    /**
     * 根據(jù)sql獲取解析引擎封裝對(duì)象
     */
    @Override
    protected SQLParseEngine getSQLParseEngine(final String sql) {
        //參數(shù)1:?jiǎn)卫?,加載statement、提取、過濾配置文件
        //參數(shù)2:數(shù)據(jù)庫(kù)類型
        //參數(shù)3:需要解析sql
        //參數(shù)4:分片表元數(shù)據(jù)
        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());
        //加對(duì)應(yīng)類型(sharding、masterslave、encrypt)配置文件
        //META-INF/parsing-rule-definition/sharding/filler-rule-definition.xml
        FillerRuleDefinitionEntity featureGeneralFillerRuleEntity = fillerRuleLoader.load(RuleDefinitionFileConstant.getFillerRuleDefinitionFile(getType()));
        //根據(jù)數(shù)據(jù)庫(kù)類型加載對(duì)應(yīng)的配置文件
        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對(duì)應(yīng)的提取器
        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();
    }

    //根據(jù)子類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,封裝成對(duì)應(yīng)的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)執(zhí)行,獲取解析樹
        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);
    }

    /**
     * 遞歸獲取所有參數(shù)占位符
     *
     * @param rootNode 根節(jié)點(diǎn)
     * @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創(chuàng)建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所有擴(kuò)展
        for (SQLParserEntry each : NewInstanceServiceLoader.newServiceInstances(SQLParserEntry.class)) {
            //判斷數(shù)據(jù)庫(kù)類型
            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-jdbc的ANTLR4 SQL用法實(shí)例

參考上圖,使用sharding parse解析模塊提取(extractor) ParserRuleContext對(duì)應(yīng)的參數(shù)封裝成Segment

8.SQLSegmentsExtractorEngine#extract,參考第七部圖,根據(jù)SQLStatementRule->tableReferences, columns, selectItems, where, predicate, groupBy, orderBy, limit, subqueryPredicate對(duì)應(yīng)的提取器,生成對(duì)應(yīng)類型的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對(duì)應(yīng)提取器,封裝成對(duì)應(yīng)對(duì)應(yīng)類型的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
        //分析九個(gè)提取器
        for (SQLSegmentExtractor each : ast.getSqlStatementRule().getExtractors()) {
            //分兩種類型
            //1.單一樹,直接提取單一RuleName下的token;參看sql解析后的語法樹對(duì)比比較清晰
            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解析后的語法樹對(duì)比比較清晰
            } 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);
        //參數(shù)個(gè)數(shù)
        result.setParametersCount(parameterMarkerCount);
        //segment
        result.getSQLSegments().addAll(sqlSegments);
        //遍歷填充對(duì)應(yīng)類型的Segment
        for (SQLSegment each : sqlSegments) {
            //根據(jù)數(shù)據(jù)庫(kù)類型、segment找到對(duì)應(yīng)填充器,來填充對(duì)應(yīng)的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) {
        //添加字段、字段約束、修改字段、字段命令,這四種填充器需要設(shè)置分片表元數(shù)據(jù)
        //主要通過分片表元數(shù)據(jù)來填充對(duì)應(yīng)的SQLStatement
        if (filler instanceof ShardingTableMetaDataAware) {
            ((ShardingTableMetaDataAware) filler).setShardingTableMetaData(shardingTableMetaData);
        }
        //如:
        //利用TableFill來填充SelectStatement#tables
        filler.fill(sqlSegment, sqlStatement);
    }
}

以上Sharding的SQL解析大概過程,解析ParserRuleContext提取封裝對(duì)應(yīng)的Segment,最后封裝SQLStatement,并根據(jù)Segment對(duì)應(yīng)的Filler來填充SQLStatement;具體如何提取、填充可以查看以下三個(gè)文件

extractor-rule-definition.xml
filler-rule-definition.xml
sql-statement-rule-definition.xml

到此,關(guān)于“sharding-jdbc的ANTLR4 SQL用法實(shí)例”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI