千家信息网

sharding-jdbc的ANTLR4 SQL用法实例

发表于:2024-11-17 作者:千家信息网编辑
千家信息网最后更新 2024年11月17日,这篇文章主要介绍"sharding-jdbc的ANTLR4 SQL用法实例",在日常操作中,相信很多人在sharding-jdbc的ANTLR4 SQL用法实例问题上存在疑惑,小编查阅了各式资料,整理
千家信息网最后更新 2024年11月17日sharding-jdbc的ANTLR4 SQL用法实例

这篇文章主要介绍"sharding-jdbc的ANTLR4 SQL用法实例",在日常操作中,相信很多人在sharding-jdbc的ANTLR4 SQL用法实例问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"sharding-jdbc的ANTLR4 SQL用法实例"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

Sharding主要利用ANTLR4来解析SQL,以mysql为例,分析源码前可以先了解以下三点:

  • 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 是否缓存解析后的结果     * @return     */    @Override    public SQLStatement parse(final String logicSQL, final boolean useCache) {        //解析前钩子,如:调用链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;    }    /**     * 根据sql获取解析引擎封装对象     */    @Override    protected SQLParseEngine getSQLParseEngine(final String sql) {        //参数1:单例,加载statement、提取、过滤配置文件        //参数2:数据库类型        //参数3:需要解析sql        //参数4:分片表元数据        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());        //加对应类型(sharding、masterslave、encrypt)配置文件        //META-INF/parsing-rule-definition/sharding/filler-rule-definition.xml        FillerRuleDefinitionEntity featureGeneralFillerRuleEntity = fillerRuleLoader.load(RuleDefinitionFileConstant.getFillerRuleDefinitionFile(getType()));        //根据数据库类型加载对应的配置文件        for (DatabaseType each : SQLParserFactory.getAddOnDatabaseTypes()) {            //META-INF/parsing-rule-definition/sharding.mysql/filler-rule-definition.xml            //databaseType:rules            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            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对应的提取器        return new SQLStatementRuleDefinition(statementRuleLoader.load(RuleDefinitionFileConstant.getSQLStatementRuleDefinitionFile(getType(), databaseType)), extractorRuleDefinition);    }

3.SQLParseEntry#parse,这里抽象SQLParseEntry,主要有不同入口(EncryptSQLParseEntry、MasterSlaveSQLParseEntry、ShardingSQLParseEntry)

@RequiredArgsConstructorpublic 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 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 getSQLStatementFromCache(final String sql, final boolean useCache) {        return useCache ? Optional.fromNullable(parsingResultCache.getSQLStatement(sql)) : Optional.absent();    }    //根据子类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,封装成对应的segment,如TableSegment、IndexSegment        Collection sqlSegments = extractorEngine.extract(ast);        Map 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)执行,获取解析树        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);    }    /**     * 递归获取所有参数占位符     *     * @param rootNode 根节点     * @return     */    private Map getParameterMarkerIndexes(final ParserRuleContext rootNode) {        Collection placeholderNodes = ExtractorUtils.getAllDescendantNodes(rootNode, RuleName.PARAMETER_MARKER);        Map result = new HashMap<>(placeholderNodes.size(), 1);        int index = 0;        for (ParserRuleContext each : placeholderNodes) {            result.put(each, index++);        }        return result;    }

6.使用SQLParserFactory#newInstance创建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所有扩展        for (SQLParserEntry each : NewInstanceServiceLoader.newServiceInstances(SQLParserEntry.class)) {            //判断数据库类型            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 parse解析模块提取(extractor) ParserRuleContext对应的参数封装成Segment

8.SQLSegmentsExtractorEngine#extract,参考第七部图,根据SQLStatementRule->tableReferences, columns, selectItems, where, predicate, groupBy, orderBy, limit, subqueryPredicate对应的提取器,生成对应类型的Segment

public final class SQLSegmentsExtractorEngine {        /**      * Extract SQL segments.     *      * @param ast SQL AST     * @return SQL segments     */    public Collection extract(final SQLAST ast) {        Collection result = new LinkedList<>();        //遍历Context对应提取器,封装成对应对应类型的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        //分析九个提取器        for (SQLSegmentExtractor each : ast.getSqlStatementRule().getExtractors()) {            //分两种类型            //1.单一树,直接提取单一RuleName下的token;参看sql解析后的语法树对比比较清晰            if (each instanceof OptionalSQLSegmentExtractor) {                Optional sqlSegment = ((OptionalSQLSegmentExtractor) each).extract(ast.getParserRuleContext(), ast.getParameterMarkerIndexes());                if (sqlSegment.isPresent()) {                    result.add(sqlSegment.get());                }            //2.分叉树,需遍历提取RuleName下的所有Token;参看sql解析后的语法树对比比较清晰            } else if (each instanceof CollectionSQLSegmentExtractor) {                result.addAll(((CollectionSQLSegmentExtractor) each).extract(ast.getParserRuleContext(), ast.getParameterMarkerIndexes()));            }        }        return result;    }}

9.SQLStatementFillerEngine#fill,封装SQLStatement,填充Segment

@RequiredArgsConstructorpublic 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 sqlSegments, final int parameterMarkerCount, final SQLStatementRule rule) {        //如SelectStatement        SQLStatement result = rule.getSqlStatementClass().newInstance();        //逻辑sql        result.setLogicSQL(sql);        //参数个数        result.setParametersCount(parameterMarkerCount);        //segment        result.getSQLSegments().addAll(sqlSegments);        //遍历填充对应类型的Segment        for (SQLSegment each : sqlSegments) {            //根据数据库类型、segment找到对应填充器,来填充对应的segment            //如:TableSegment->TableFiller            Optional 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) {        //添加字段、字段约束、修改字段、字段命令,这四种填充器需要设置分片表元数据        //主要通过分片表元数据来填充对应的SQLStatement        if (filler instanceof ShardingTableMetaDataAware) {            ((ShardingTableMetaDataAware) filler).setShardingTableMetaData(shardingTableMetaData);        }        //如:        //利用TableFill来填充SelectStatement#tables        filler.fill(sqlSegment, sqlStatement);    }}

以上Sharding的SQL解析大概过程,解析ParserRuleContext提取封装对应的Segment,最后封装SQLStatement,并根据Segment对应的Filler来填充SQLStatement;具体如何提取、填充可以查看以下三个文件

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

到此,关于"sharding-jdbc的ANTLR4 SQL用法实例"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0