antlr4 mysql_sharding-jdbc之ANTLR4 SQL解析
公众号:帽爹的技术轮子
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解析相关推荐
- mysql词法分析antlr4_sharding-jdbc之ANTLR4 SQL解析
公众号:帽爹的技术轮子 Sharding主要利用ANTLR4来解析SQL,以mysql为例,分析源码前可以先了解以下三点: 源码分析 1.解析入口ParsingSQLRouter#parse /** ...
- mysql实现vpd_基于JDBC实现VPD:SQL解析篇
接着之前的文章<浅谈基于JDBC实现虚拟专用数据库(VPD)>的内容,今天我们重点来说一下SQL解析的问题. 从架构上我们可以看出来,如果要基于JDBC做VPD,不能绕开的一个问题,就是要 ...
- 如何实现一个SQL解析器
1. 背景 随着技术的不断的发展,在大数据领域出现了越来越多的技术框架.而为了降低大数据的学习成本和难度,越来越多的大数据技术和应用开始支持SQL进行数据查询.SQL作为一个学习成本很低的语言,支持S ...
- sharding-JDBC源码分析(二)SQL解析
SQL parser SQL解析是根据语法与词法分析SQL,理解SQL含义,才能按照SQL语义处理数据,SQL解析是实现分库分表组件最基础的功能,熟悉Mysql架构的,内部也有很重要的一个模块就是SQ ...
- Spark之SQL解析(源码阅读十)
如何能更好的运用与监控sparkSQL?或许我们改更深层次的了解它深层次的原理是什么.之前总结的已经写了传统数据库与Spark的sql解析之间的差别.那么我们下来直切主题~ 如今的Spark已经支持多 ...
- java mysql 语句解析器_几种基于Java的SQL解析工具的比较与调用
1.sqlparser http://www.sqlparser.com/ 优点:支持的数据库最多,除了传统数据库外还支持hive和greenplum一类比较新的数据库,调用比较方便,功能不错 缺点: ...
- TANX英文翻译软件测试工资,ktanx-jdbc 1.0.5发布,增加自定义sql解析及部分方法修改...
版本 1.0.5 更新内容: 增加自定义sql解析转换,使用native方式执行自定义sql时可以在sql中使用类名和类属性了. resultClass方法增强,支持JavaBean外的基本类型,例如 ...
- jdbc桥连接过程解析
2019独角兽企业重金招聘Python工程师标准>>> 读多少源码,便知自己有多无知! 想温习一下桥链接模式,然后觉得自己已然吃透了,因为自己写的博客,觉得还是应该更具体一些. 类似 ...
- bs架构与cs架构的区别_Oracle vs Mysql--架构、sql查询执行流程及SQL解析顺序区别说明...
概述 之前分享的主要是Oracle上的一些内容,那么mysql又有哪些地方不一样呢?下面从MySQL总体架构.sql查询执行流程和语句执行顺序来看一下.. 01 架构总览 下面看一下mysql的架构图 ...
最新文章
- script的加载方式与执行
- Java基础(三十五)Math、Random类和数字格式化(String.format方法)
- 大剑无锋之Hive调优【面试推荐】
- 使用POI 读取 Excel 文件,读取手机号码 变成 1.3471022771E10
- OS开发UI篇—Quartz2D使用(截屏)
- 在VMware workstation 9.0中安装Windows server 2012 和 Hyper-v(虚拟机中安装虚拟机)
- mysql单张表数据量极限_极限数据量范围的安全测试
- 详解 TCP/IP 协议
- 首次将项目从eclipse提交到服务器的SVN
- 控制系统数字仿真与CAD-第四次实验-附完整代码
- DateFormat与SimpleDateFormat区别和使用详解
- Java程序员简历模板
- 一、Fiddler抓包工具 — Fiddler介绍与安装
- WebRTC-NACK、Pacer和拥塞控制和FEC
- 如何用cocos2d-x来开发简单的Uphone游戏:(三) 射击子弹 碰撞检测
- 若已知1800年1月1日为星期3,则对于一个给定的年份和月份,输出这个月的最后一天是星期几。
- 高性能MySQL之 Chapter13
- 【20210913】【数据库】用SQL语句,往数据库里插入一行、多行新数据
- wifi测试软件推荐,推荐4个专业又实用的WiFi检测工具,了解一下
- 【UCIe】UCIe 支持的协议及操作模式