公众号:帽爹的技术轮子

Sharding主要利用ANTLR4来解析SQL,以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) @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 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 extends SQLSegment> 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 @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 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

antlr4 mysql_sharding-jdbc之ANTLR4 SQL解析相关推荐

  1. mysql词法分析antlr4_sharding-jdbc之ANTLR4 SQL解析

    公众号:帽爹的技术轮子 Sharding主要利用ANTLR4来解析SQL,以mysql为例,分析源码前可以先了解以下三点: 源码分析 1.解析入口ParsingSQLRouter#parse /** ...

  2. mysql实现vpd_基于JDBC实现VPD:SQL解析篇

    接着之前的文章<浅谈基于JDBC实现虚拟专用数据库(VPD)>的内容,今天我们重点来说一下SQL解析的问题. 从架构上我们可以看出来,如果要基于JDBC做VPD,不能绕开的一个问题,就是要 ...

  3. 如何实现一个SQL解析器

    1. 背景 随着技术的不断的发展,在大数据领域出现了越来越多的技术框架.而为了降低大数据的学习成本和难度,越来越多的大数据技术和应用开始支持SQL进行数据查询.SQL作为一个学习成本很低的语言,支持S ...

  4. sharding-JDBC源码分析(二)SQL解析

    SQL parser SQL解析是根据语法与词法分析SQL,理解SQL含义,才能按照SQL语义处理数据,SQL解析是实现分库分表组件最基础的功能,熟悉Mysql架构的,内部也有很重要的一个模块就是SQ ...

  5. Spark之SQL解析(源码阅读十)

    如何能更好的运用与监控sparkSQL?或许我们改更深层次的了解它深层次的原理是什么.之前总结的已经写了传统数据库与Spark的sql解析之间的差别.那么我们下来直切主题~ 如今的Spark已经支持多 ...

  6. java mysql 语句解析器_几种基于Java的SQL解析工具的比较与调用

    1.sqlparser http://www.sqlparser.com/ 优点:支持的数据库最多,除了传统数据库外还支持hive和greenplum一类比较新的数据库,调用比较方便,功能不错 缺点: ...

  7. TANX英文翻译软件测试工资,ktanx-jdbc 1.0.5发布,增加自定义sql解析及部分方法修改...

    版本 1.0.5 更新内容: 增加自定义sql解析转换,使用native方式执行自定义sql时可以在sql中使用类名和类属性了. resultClass方法增强,支持JavaBean外的基本类型,例如 ...

  8. jdbc桥连接过程解析

    2019独角兽企业重金招聘Python工程师标准>>> 读多少源码,便知自己有多无知! 想温习一下桥链接模式,然后觉得自己已然吃透了,因为自己写的博客,觉得还是应该更具体一些. 类似 ...

  9. bs架构与cs架构的区别_Oracle vs Mysql--架构、sql查询执行流程及SQL解析顺序区别说明...

    概述 之前分享的主要是Oracle上的一些内容,那么mysql又有哪些地方不一样呢?下面从MySQL总体架构.sql查询执行流程和语句执行顺序来看一下.. 01 架构总览 下面看一下mysql的架构图 ...

最新文章

  1. script的加载方式与执行
  2. Java基础(三十五)Math、Random类和数字格式化(String.format方法)
  3. 大剑无锋之Hive调优【面试推荐】
  4. 使用POI 读取 Excel 文件,读取手机号码 变成 1.3471022771E10
  5. OS开发UI篇—Quartz2D使用(截屏)
  6. 在VMware workstation 9.0中安装Windows server 2012 和 Hyper-v(虚拟机中安装虚拟机)
  7. mysql单张表数据量极限_极限数据量范围的安全测试
  8. 详解 TCP/IP 协议
  9. 首次将项目从eclipse提交到服务器的SVN
  10. 控制系统数字仿真与CAD-第四次实验-附完整代码
  11. DateFormat与SimpleDateFormat区别和使用详解
  12. Java程序员简历模板
  13. 一、Fiddler抓包工具 — Fiddler介绍与安装
  14. WebRTC-NACK、Pacer和拥塞控制和FEC
  15. 如何用cocos2d-x来开发简单的Uphone游戏:(三) 射击子弹 碰撞检测
  16. 若已知1800年1月1日为星期3,则对于一个给定的年份和月份,输出这个月的最后一天是星期几。
  17. 高性能MySQL之 Chapter13
  18. 【20210913】【数据库】用SQL语句,往数据库里插入一行、多行新数据
  19. wifi测试软件推荐,推荐4个专业又实用的WiFi检测工具,了解一下
  20. 【UCIe】UCIe 支持的协议及操作模式

热门文章

  1. CFF Explorer实现Windows 7下API HOOK
  2. 微信公众号【阿里技术(ali_tech)】历史文章整理
  3. ChatGPT能接入微信了!
  4. mvnw,到底是什么鬼?
  5. CSS 强制不换行,文字溢出显示省略号~
  6. cximage库实现图片重叠效果
  7. Python MOOC练习3
  8. ElasticSearch -- ES 7.x 集群版安装部署
  9. 如果流星拖着长长的尾
  10. 长连接与短连接、全双工与半双工,单工