【源码】Sharding-JDBC源码分析之Sql解析的原理

Sharding-JDBC系列

1、Sharding-JDBC分库分表的基本使用

2、Sharding-JDBC分库分表之SpringBoot分片策略

3、Sharding-JDBC分库分表之SpringBoot主从配置

4、SpringBoot集成Sharding-JDBC-5.3.0分库分表

5、SpringBoot集成Sharding-JDBC-5.3.0实现按月动态建表分表

6、【源码】Sharding-JDBC源码分析之JDBC

7、【源码】Sharding-JDBC源码分析之SPI机制

8、【源码】Sharding-JDBC源码分析之Yaml分片配置文件解析原理

9、【源码】Sharding-JDBC源码分析之Yaml分片配置原理(一)

10、【源码】Sharding-JDBC源码分析之Yaml分片配置原理(二)

11、【源码】Sharding-JDBC源码分析之Yaml分片配置转换原理

12、【源码】Sharding-JDBC源码分析之ShardingSphereDataSource的创建原理

13、【源码】Sharding-JDBC源码分析之ContextManager创建中mode分片配置信息的持久化存储的原理

14、【源码】Sharding-JDBC源码分析之ContextManager创建中ShardingSphereDatabase的创建原理

15、【源码】Sharding-JDBC源码分析之分片规则生成器DatabaseRuleBuilder实现规则配置到规则对象的生成原理

16、【源码】Sharding-JDBC源码分析之配置数据库定义的表的元数据解析原理

17、【源码】Sharding-JDBC源码分析之ShardingSphereConnection的创建原理

18、【源码】Sharding-JDBC源码分析之Sql解析的原理

前言

Sharding-JDBC是一个开源的分库分表中间件,它通过自动解析SQL,结合配置的分库分表规则,重写SQL,自动的将SQL路由到正确的数据库和表。Sharding-JDBC是通过Antlr来解析SQL的。本篇以MySQL数据库为例,从源码的角度分析SQL解析的实现原理。

Antlr详见:Antlr的使用-CSDN博客

MySQLStatement.g4

Antlr 使用时,需先创建解析文本的语法规则文件,文件以 .g4 后缀结尾。文件在源码的 sql-parser 文件夹下。支持的数据库如下图所示:

对于MySQL数据库,其语法规则文件为MySQLStatement.g4,在

MySQLStatement.g4的部分源码如下:

Groovy 复制代码
grammar MySQLStatement;

import Comments, DDLStatement, TCLStatement, DCLStatement, RLStatement;

execute
    : (select    // 查询
    | insert     // 插入
    | update
    | delete
    | replace

// 省略其他

    | delimiter
    ) (SEMI_ EOF? | EOF)
    | EOF
    ;

在Sharding-JDBC中,支持了几乎所有的MySQL数据库操作,对于最常用的DML,其规则文件为 DMLStatement.g4。以下以select语句为例进行分析。

DMLStatement.g4

DMLStatement.g4的部分源码如下:

Groovy 复制代码
grammar DMLStatement;

import BaseRule;

// 查询语句
select
    : queryExpression lockClauseList?  // 查询表达式
    | queryExpressionParens
    | selectWithInto
    ;

// 查询表达式
queryExpression
    : withClause? (queryExpressionBody | queryExpressionParens) orderByClause? limitClause?
    ;

// 查询表达式体
queryExpressionBody
    : queryPrimary     // 主查询
    | queryExpressionParens combineClause
    | queryExpressionBody combineClause
    ;

combineClause
    : UNION combineOption? (queryPrimary | queryExpressionParens)
    ;

queryExpressionParens
    : LP_ (queryExpressionParens | queryExpression lockClauseList?) RP_
    ;

// 主查询
queryPrimary
    : querySpecification
    | tableValueConstructor
    | tableStatement
    ;

// 查询规则
querySpecification
    : SELECT selectSpecification* projections selectIntoExpression? fromClause? whereClause? groupByClause? havingClause? windowClause?
    ;

// where部分
whereClause
    : WHERE expr
    ;

// 投影部分
projections
    : (unqualifiedShorthand | projection) (COMMA_ projection)*
    ;

projection
    : expr (AS? alias)? | qualifiedShorthand
    ;


// 省略其他

DMLStatement.g4 中引入了BaseRule.g4的内容。通过 querySpecification 规则,对于select语句,需要以select开头。

BaseRule.g4

BaseRule.g4的部分源码如下:

Groovy 复制代码
// 表达式
expr
    : booleanPrimary   // 表达式主
    | expr andOperator expr  // and 表达式
    | expr orOperator expr
    | expr XOR expr
    | notOperator expr
    ;
    
andOperator
    : AND | AND_
    ;
    
orOperator
    : OR | OR_
    ;
    
notOperator
    : NOT | NOT_
    ;
    
booleanPrimary
    : booleanPrimary IS NOT? (TRUE | FALSE | UNKNOWN | NULL)
    | booleanPrimary SAFE_EQ_ predicate
    | booleanPrimary comparisonOperator predicate    // =、> 等表达式
    | booleanPrimary comparisonOperator (ALL | ANY) subquery
    | booleanPrimary assignmentOperator predicate
    | predicate
    ;
    
assignmentOperator
    : EQ_ | ASSIGNMENT_
    ;
    
comparisonOperator
    : EQ_ | GTE_ | GT_ | LTE_ | LT_ | NEQ_
    ;
    
predicate
    : bitExpr NOT? IN subquery
    | bitExpr NOT? IN LP_ expr (COMMA_ expr)* RP_
    | bitExpr NOT? BETWEEN bitExpr AND predicate
    | bitExpr SOUNDS LIKE bitExpr
    | bitExpr NOT? LIKE simpleExpr (ESCAPE simpleExpr)?
    | bitExpr NOT? REGEXP bitExpr
    | bitExpr
    ;
    
bitExpr
    : bitExpr VERTICAL_BAR_ bitExpr
    | bitExpr AMPERSAND_ bitExpr
    | bitExpr SIGNED_LEFT_SHIFT_ bitExpr
    | bitExpr SIGNED_RIGHT_SHIFT_ bitExpr
    | bitExpr PLUS_ bitExpr
    | bitExpr MINUS_ bitExpr
    | bitExpr ASTERISK_ bitExpr
    | bitExpr SLASH_ bitExpr
    | bitExpr DIV bitExpr
    | bitExpr MOD bitExpr
    | bitExpr MOD_ bitExpr
    | bitExpr CARET_ bitExpr
    | bitExpr PLUS_ intervalExpression
    | bitExpr MINUS_ intervalExpression
    | simpleExpr
    ;
    

简单示例

以下以一个select语句为例:

select id, name from tb_user where id = 1

匹配 DMLStatement.g4 中的 querySpecification 规则,匹配如下:

MySQLStatementSQLVisitor

基于 MySQLStatement.g4 文件,通过Anltr的Generate ANTLR Recognizer,自动生成MySQLStatementBaseVisitor.java文件。MySQLStatementSQLVisitor继承MySQLStatementBaseVisitor,通过重写对应的 visit 方法,创建SQL解析后的对象。

MySQLStatementSQLVisitor源码

java 复制代码
package org.apache.shardingsphere.sql.parser.mysql.visitor.statement.impl;

/**
 * MySQL的sql语句的访问器
 */
@NoArgsConstructor
@Getter(AccessLevel.PROTECTED)
public abstract class MySQLStatementSQLVisitor extends MySQLStatementBaseVisitor<ASTNode> {
    
    // 当前参数下标。当解析参数的时候,会自动累加
    private int currentParameterIndex;
    
    // 参数标记部分。当解析参数的时候,会自动创建一个对象,加入到集合中
    private final Collection<ParameterMarkerSegment> parameterMarkerSegments = new LinkedList<>();
    
    public MySQLStatementSQLVisitor(final Properties props) {
    }

    /**
     * 访问参数标记。返回一个参数标识值对象,记录下标和?
     * @param ctx
     * @return
     */
    @Override
    public final ASTNode visitParameterMarker(final ParameterMarkerContext ctx) {
        return new ParameterMarkerValue(currentParameterIndex++, ParameterMarkerType.QUESTION);
    }
 
    
    @Override
    public final ASTNode visitIdentifier(final IdentifierContext ctx) {
        return new IdentifierValue(ctx.getText());
    }


    /**
     * 访问表名
     * @param ctx
     * @return
     */
    @Override
    public final ASTNode visitTableName(final TableNameContext ctx) {
        // 根据ctx,创建简单表部分
        SimpleTableSegment result = new SimpleTableSegment(new TableNameSegment(ctx.name().getStart().getStartIndex(),
                ctx.name().getStop().getStopIndex(), new IdentifierValue(ctx.name().identifier().getText())));
        // 获取owner
        OwnerContext owner = ctx.owner();
        if (null != owner) {
            // 访问owner部分
            result.setOwner((OwnerSegment) visit(owner));
        }
        return result;
    }

    
    @Override
    public final ASTNode visitOwner(final OwnerContext ctx) {
        return new OwnerSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), (IdentifierValue) visit(ctx.identifier()));
    }
 

    /**
     * 访问表达式
     * @param ctx
     * @return
     */
    @Override
    public final ASTNode visitExpr(final ExprContext ctx) {
        // 普通的表达式都为booleanPrimary
        if (null != ctx.booleanPrimary()) {
            return visit(ctx.booleanPrimary());
        }
        // xor 连接的表达式
        if (null != ctx.XOR()) {
            return createBinaryOperationExpression(ctx, "XOR");
        }
        // and 连接的表达式
        if (null != ctx.andOperator()) {
            return createBinaryOperationExpression(ctx, ctx.andOperator().getText());
        }
        // or 连接的表达式
        if (null != ctx.orOperator()) {
            return createBinaryOperationExpression(ctx, ctx.orOperator().getText());
        }
        return new NotExpression(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), (ExpressionSegment) visit(ctx.expr(0)));
    }
    
    private BinaryOperationExpression createBinaryOperationExpression(final ExprContext ctx, final String operator) {
        ExpressionSegment left = (ExpressionSegment) visit(ctx.expr(0));
        ExpressionSegment right = (ExpressionSegment) visit(ctx.expr(1));
        String text = ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex()));
        return new BinaryOperationExpression(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), left, right, operator, text);
    }

    /**
     * 普通表达式访问
     * @param ctx
     * @return
     */
    @Override
    public final ASTNode visitBooleanPrimary(final BooleanPrimaryContext ctx) {
        // 带 is 的条件表达式
        if (null != ctx.IS()) {
            // TODO optimize operatorToken
            String rightText = "";
            if (null != ctx.NOT()) {
                rightText = rightText.concat(ctx.start.getInputStream().getText(new Interval(ctx.NOT().getSymbol().getStartIndex(),
                        ctx.NOT().getSymbol().getStopIndex()))).concat(" ");
            }
            Token operatorToken = null;
            if (null != ctx.NULL()) {
                operatorToken = ctx.NULL().getSymbol();
            }
            if (null != ctx.TRUE()) {
                operatorToken = ctx.TRUE().getSymbol();
            }
            if (null != ctx.FALSE()) {
                operatorToken = ctx.FALSE().getSymbol();
            }
            int startIndex = null == operatorToken ? ctx.IS().getSymbol().getStopIndex() + 2 : operatorToken.getStartIndex();
            rightText = rightText.concat(ctx.start.getInputStream().getText(new Interval(startIndex, ctx.stop.getStopIndex())));
            ExpressionSegment right = new LiteralExpressionSegment(ctx.IS().getSymbol().getStopIndex() + 2, ctx.stop.getStopIndex(), rightText);
            String text = ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex()));
            ExpressionSegment left = (ExpressionSegment) visit(ctx.booleanPrimary());
            String operator = "IS";
            return new BinaryOperationExpression(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), left, right, operator, text);
        }
        // 比较运算符的表达式
        if (null != ctx.comparisonOperator() || null != ctx.SAFE_EQ_()) {
            return createCompareSegment(ctx);
        }
        // 作业操作符的表达式
        if (null != ctx.assignmentOperator()) {
            return createAssignmentSegment(ctx);
        }
        // 谓词的访问
        return visit(ctx.predicate());
    }
    
    private ASTNode createAssignmentSegment(final BooleanPrimaryContext ctx) {
        ExpressionSegment left = (ExpressionSegment) visit(ctx.booleanPrimary());
        ExpressionSegment right = (ExpressionSegment) visit(ctx.predicate());
        String operator = ctx.assignmentOperator().getText();
        String text = ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex()));
        return new BinaryOperationExpression(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), left, right, operator, text);
    }

    /**
     * 创建比较表达式部分
     * @param ctx
     * @return
     */
    private ASTNode createCompareSegment(final BooleanPrimaryContext ctx) {
        // 获取左边的表达式
        ExpressionSegment left = (ExpressionSegment) visit(ctx.booleanPrimary());
        ExpressionSegment right;
        // 获取右边的表达式
        if (null != ctx.predicate()) {
            right = (ExpressionSegment) visit(ctx.predicate());
        } else {
            // 子查询表达式部分
            right = new SubqueryExpressionSegment(new SubquerySegment(ctx.subquery().start.getStartIndex(), ctx.subquery().stop.getStopIndex(), (MySQLSelectStatement) visit(ctx.subquery())));
        }
        // 获取表达式运算符
        String operator = null != ctx.SAFE_EQ_() ? ctx.SAFE_EQ_().getText() : ctx.comparisonOperator().getText();
        // 获取表达式对应的文本
        String text = ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex()));
        // 创建二元运算表达式对象
        return new BinaryOperationExpression(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), left, right, operator, text);
    }
    
    @Override
    public final ASTNode visitPredicate(final PredicateContext ctx) {
        // 创建 in 部分
        if (null != ctx.IN()) {
            return createInSegment(ctx);
        }
        // 创建between
        if (null != ctx.BETWEEN()) {
            return createBetweenSegment(ctx);
        }
        if (null != ctx.LIKE()) {
            return createBinaryOperationExpressionFromLike(ctx);
        }
        if (null != ctx.REGEXP()) {
            return createBinaryOperationExpressionFromRegexp(ctx);
        }
        // 创建bit表达式,即文本等
        return visit(ctx.bitExpr(0));
    }

    /**
     * 访问字节表达式
     * @param ctx
     * @return
     */
    @Override
    public final ASTNode visitBitExpr(final BitExprContext ctx) {
        // 如果有简单字节表达式,即文本,则访问
        if (null != ctx.simpleExpr()) {
            return visit(ctx.simpleExpr());
        }
        // 二元运算表达式访问
        ExpressionSegment left = (ExpressionSegment) visit(ctx.getChild(0));
        ExpressionSegment right = (ExpressionSegment) visit(ctx.getChild(2));
        String operator = ctx.getChild(1).getText();
        String text = ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex()));
        return new BinaryOperationExpression(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), left, right, operator, text);
    }

    /**
     * 访问简单表达式
     * @param ctx
     * @return
     */
    @Override
    public final ASTNode visitSimpleExpr(final SimpleExprContext ctx) {
        // 获取起始、截止位置
        int startIndex = ctx.start.getStartIndex();
        int stopIndex = ctx.stop.getStopIndex();
        // 访问子查询
        if (null != ctx.subquery()) {
            SubquerySegment subquerySegment = new SubquerySegment(ctx.subquery().getStart().getStartIndex(), ctx.subquery().getStop().getStopIndex(), (MySQLSelectStatement) visit(ctx.subquery()));
            if (null != ctx.EXISTS()) {
                return new ExistsSubqueryExpression(startIndex, stopIndex, subquerySegment);
            }
            return new SubqueryExpressionSegment(subquerySegment);
        }
        // 访问参数标记
        if (null != ctx.parameterMarker()) {
            // 访问参数标记,创建一个ParameterMarkerValue,记录当前参数的下标及?
            ParameterMarkerValue parameterMarker = (ParameterMarkerValue) visit(ctx.parameterMarker());
            // 创建参数标记表达式部分
            ParameterMarkerExpressionSegment segment = new ParameterMarkerExpressionSegment(startIndex, stopIndex, parameterMarker.getValue(), parameterMarker.getType());
            // 记录,并返回
            parameterMarkerSegments.add(segment);
            return segment;
        }
        // 访问字面量
        if (null != ctx.literals()) {
            return SQLUtil.createLiteralExpression(visit(ctx.literals()), startIndex, stopIndex, ctx.literals().start.getInputStream().getText(new Interval(startIndex, stopIndex)));
        }
        // 访问区间表达式
        if (null != ctx.intervalExpression()) {
            return visit(ctx.intervalExpression());
        }
        // 访问方法调用
        if (null != ctx.functionCall()) {
            return visit(ctx.functionCall());
        }
        if (null != ctx.collateClause()) {
            SimpleExpressionSegment collateValueSegment = (SimpleExpressionSegment) visit(ctx.collateClause());
            return new CollateExpression(startIndex, stopIndex, collateValueSegment);
        }
        // 访问列
        if (null != ctx.columnRef()) {
            return visit(ctx.columnRef());
        }
        // 访问匹配表达式
        if (null != ctx.matchExpression()) {
            return visit(ctx.matchExpression());
        }
        // not 运算符
        if (null != ctx.notOperator()) {
            ASTNode expression = visit(ctx.simpleExpr(0));
            if (expression instanceof ExistsSubqueryExpression) {
                ((ExistsSubqueryExpression) expression).setNot(true);
                return expression;
            }
            return new NotExpression(startIndex, stopIndex, (ExpressionSegment) expression);
        }
        if (null != ctx.LP_() && 1 == ctx.expr().size()) {
            return visit(ctx.expr(0));
        }
        return visitRemainSimpleExpr(ctx);
    }

    /**
     * 访问列
     * @param ctx
     * @return
     */
    @Override
    public ASTNode visitColumnRef(final ColumnRefContext ctx) {
        int identifierCount = ctx.identifier().size();
        ColumnSegment result;
        // 只有一个标识符
        if (1 == identifierCount) {
            // 创建一个列部分
            result = new ColumnSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), (IdentifierValue) visit(ctx.identifier(0)));
        } else if (2 == identifierCount) {
            // 有两个标识符,后一个为列部分,前一个为列的owner部分
            result = new ColumnSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), (IdentifierValue) visit(ctx.identifier(1)));
            result.setOwner(new OwnerSegment(ctx.identifier(0).start.getStartIndex(), ctx.identifier(0).stop.getStopIndex(), (IdentifierValue) visit(ctx.identifier(0))));
        } else {
            // 如果有三个,则为owner.owner.col
            result = new ColumnSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), (IdentifierValue) visit(ctx.identifier(2)));
            OwnerSegment owner = new OwnerSegment(ctx.identifier(1).start.getStartIndex(), ctx.identifier(1).stop.getStopIndex(), (IdentifierValue) visit(ctx.identifier(1)));
            owner.setOwner(new OwnerSegment(ctx.identifier(0).start.getStartIndex(), ctx.identifier(0).stop.getStopIndex(), (IdentifierValue) visit(ctx.identifier(0))));
            result.setOwner(owner);
        }
        return result;
    }

    @Override
    public ASTNode visitQueryExpression(final QueryExpressionContext ctx) {
        MySQLSelectStatement result;
        if (null != ctx.queryExpressionBody()) {
            result = (MySQLSelectStatement) visit(ctx.queryExpressionBody());
        } else {
            result = (MySQLSelectStatement) visit(ctx.queryExpressionParens());
        }
        if (null != ctx.orderByClause()) {
            result.setOrderBy((OrderBySegment) visit(ctx.orderByClause()));
        }
        if (null != ctx.limitClause()) {
            result.setLimit((LimitSegment) visit(ctx.limitClause()));
        }
        return result;
    }
    
    /**
     * 访问查询表达式体
     * @param ctx
     * @return
     */
    @Override
    public ASTNode visitQueryExpressionBody(final QueryExpressionBodyContext ctx) {
        // 如果只有一个子节点 && 节点为QueryPrimaryContext,即queryPrimary部分
        if (1 == ctx.getChildCount() && ctx.getChild(0) instanceof QueryPrimaryContext) {
            return visit(ctx.queryPrimary());
        }
        // 如果是queryExpressionBody,递归访问该方法
        if (null != ctx.queryExpressionBody()) {
            MySQLSelectStatement result = new MySQLSelectStatement();
            MySQLSelectStatement left = (MySQLSelectStatement) visit(ctx.queryExpressionBody());
            result.setProjections(left.getProjections());
            result.setFrom(left.getFrom());
            left.getTable().ifPresent(result::setTable);
            result.setCombine(createCombineSegment(ctx.combineClause(), left));
            return result;
        }
        return visit(ctx.queryExpressionParens());
    }
    
    private CombineSegment createCombineSegment(final CombineClauseContext ctx, final MySQLSelectStatement left) {
        CombineType combineType = (null != ctx.combineOption() && null != ctx.combineOption().ALL()) ? CombineType.UNION_ALL : CombineType.UNION;
        MySQLSelectStatement right = null != ctx.queryPrimary() ? (MySQLSelectStatement) visit(ctx.queryPrimary()) : (MySQLSelectStatement) visit(ctx.queryExpressionParens());
        return new CombineSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), left, combineType, right);
    }

    /**
     * 访问查询规则
     * @param ctx
     * @return
     */
    @Override
    public ASTNode visitQuerySpecification(final QuerySpecificationContext ctx) {
        MySQLSelectStatement result = new MySQLSelectStatement();
        // 访问投影
        result.setProjections((ProjectionsSegment) visit(ctx.projections()));
        // 访问查询规则部分
        if (null != ctx.selectSpecification()) {
            result.getProjections().setDistinctRow(isDistinct(ctx));
        }
        // from的访问,获取表部分
        if (null != ctx.fromClause() && null != ctx.fromClause().tableReferences()) {
            TableSegment tableSource = (TableSegment) visit(ctx.fromClause().tableReferences());
            result.setFrom(tableSource);
        }
        // where的访问,获取where条件部分
        if (null != ctx.whereClause()) {
            result.setWhere((WhereSegment) visit(ctx.whereClause()));
        }
        if (null != ctx.groupByClause()) {
            result.setGroupBy((GroupBySegment) visit(ctx.groupByClause()));
        }
        if (null != ctx.havingClause()) {
            result.setHaving((HavingSegment) visit(ctx.havingClause()));
        }
        if (null != ctx.windowClause()) {
            result.setWindow((WindowSegment) visit(ctx.windowClause()));
        }
        return result;
    }
    
    @Override
    public ASTNode visitTableStatement(final TableStatementContext ctx) {
        MySQLSelectStatement result = new MySQLSelectStatement();
        result.setTable((SimpleTableSegment) visit(ctx.tableName()));
        return result;
    }

    /**
     * 访问排序部分
     * @param ctx
     * @return
     */
    @Override
    public final ASTNode visitOrderByClause(final OrderByClauseContext ctx) {
        Collection<OrderByItemSegment> items = new LinkedList<>();
        // 访问排序项
        for (OrderByItemContext each : ctx.orderByItem()) {
            items.add((OrderByItemSegment) visit(each));
        }
        return new OrderBySegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), items);
    }

    /**
     * 访问排序项
     * @param ctx
     * @return
     */
    @Override
    public final ASTNode visitOrderByItem(final OrderByItemContext ctx) {
        OrderDirection orderDirection;
        // 排序方向部分
        if (null != ctx.direction()) {
            orderDirection = null != ctx.direction().DESC() ? OrderDirection.DESC : OrderDirection.ASC;
        } else {
            orderDirection = OrderDirection.ASC;
        }
        // 数字部分
        if (null != ctx.numberLiterals()) {
            return new IndexOrderByItemSegment(ctx.numberLiterals().getStart().getStartIndex(), ctx.numberLiterals().getStop().getStopIndex(),
                    SQLUtil.getExactlyNumber(ctx.numberLiterals().getText(), 10).intValue(), orderDirection, null);
        } else {
            // 访问排序表达式,如排序的列等的访问
            ASTNode expr = visitExpr(ctx.expr());
            // 如果为列
            if (expr instanceof ColumnSegment) {
                // 创建排序列部分
                return new ColumnOrderByItemSegment((ColumnSegment) expr, orderDirection, null);
            } else {
                // 如果是表达式,创建排序表达式部分
                return new ExpressionOrderByItemSegment(ctx.expr().getStart().getStartIndex(),
                        ctx.expr().getStop().getStopIndex(), getOriginalText(ctx.expr()), orderDirection, null, (ExpressionSegment) expr);
            }
        }
    }

    @Override
    public ASTNode visitSingleTableClause(final SingleTableClauseContext ctx) {
        SimpleTableSegment result = (SimpleTableSegment) visit(ctx.tableName());
        if (null != ctx.alias()) {
            result.setAlias((AliasSegment) visit(ctx.alias()));
        }
        return result;
    }
    
    /**
     * Select语句
     * @param ctx
     * @return
     */
    @Override
    public ASTNode visitSelect(final SelectContext ctx) {
        // TODO :Unsupported for withClause.
        MySQLSelectStatement result;
        if (null != ctx.queryExpression()) {
            // 如果有查询表达式,访问表达式,获得MySQLSelectStatement对象
            result = (MySQLSelectStatement) visit(ctx.queryExpression());
            // 如果有锁定条款列表,访问作为锁部分,添加到MySQLSelectStatement对象
            if (null != ctx.lockClauseList()) {
                result.setLock((LockSegment) visit(ctx.lockClauseList()));
            }
        } else if (null != ctx.selectWithInto()) {
            // 访问selectWithInto部分
            result = (MySQLSelectStatement) visit(ctx.selectWithInto());
        } else {
            // 如果是queryExpressionParens,访问第一个孩子节点
            result = (MySQLSelectStatement) visit(ctx.getChild(0));
        }
        // 设置参数数量,在解析参数标记时,会自动从0开始累加currentParameterIndex
        result.setParameterCount(currentParameterIndex);
        // 添加参数标记部分,在解析参数标记时,会创建参数标记部分对象,加入到集合中
        result.getParameterMarkerSegments().addAll(getParameterMarkerSegments());
        return result;
    }

    /**
     * 如 distinct 等的访问
     * @param ctx
     * @return
     */
    private boolean isDistinct(final QuerySpecificationContext ctx) {
        // 包括:duplicateSpecification | HIGH_PRIORITY | STRAIGHT_JOIN | SQL_SMALL_RESULT |
        // SQL_BIG_RESULT | SQL_BUFFER_RESULT | SQL_NO_CACHE | SQL_CALC_FOUND_ROWS
        for (SelectSpecificationContext each : ctx.selectSpecification()) {
            if (((BooleanLiteralValue) visit(each)).getValue()) {
                return true;
            }
        }
        return false;
    }

    /**
     * 选择规范访问
     * @param ctx
     * @return
     */
    @Override
    public ASTNode visitSelectSpecification(final SelectSpecificationContext ctx) {
        // 重复规范
        if (null != ctx.duplicateSpecification()) {
            return visit(ctx.duplicateSpecification());
        }
        return new BooleanLiteralValue(false);
    }

    /**
     * 访问重复规范
     * @param ctx
     * @return
     */
    @Override
    public ASTNode visitDuplicateSpecification(final DuplicateSpecificationContext ctx) {
        String text = ctx.getText();
        if ("DISTINCT".equalsIgnoreCase(text) || "DISTINCTROW".equalsIgnoreCase(text)) {
            return new BooleanLiteralValue(true);
        }
        return new BooleanLiteralValue(false);
    }

    /**
     * 访问投影信息
     * @param ctx
     * @return
     */
    @Override
    public ASTNode visitProjections(final ProjectionsContext ctx) {
        Collection<ProjectionSegment> projections = new LinkedList<>();
        // 如果没有特别的短语,创建一个速记投影段
        if (null != ctx.unqualifiedShorthand()) {
            projections.add(new ShorthandProjectionSegment(ctx.unqualifiedShorthand().getStart().getStartIndex(), ctx.unqualifiedShorthand().getStop().getStopIndex()));
        }
        // 遍历投影,访问投影部分
        for (ProjectionContext each : ctx.projection()) {
            projections.add((ProjectionSegment) visit(each));
        }
        // 创建投影部分对象
        ProjectionsSegment result = new ProjectionsSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex());
        // 添加投影
        result.getProjections().addAll(projections);
        return result;
    }
    
    @Override
    public ASTNode visitProjection(final ProjectionContext ctx) {
        // FIXME :The stop index of project is the stop index of projection, instead of alias.
        if (null != ctx.qualifiedShorthand()) {
            return createShorthandProjection(ctx.qualifiedShorthand());
        }
        AliasSegment alias = null == ctx.alias() ? null : (AliasSegment) visit(ctx.alias());
        ASTNode exprProjection = visit(ctx.expr());
        if (exprProjection instanceof ColumnSegment) {
            ColumnProjectionSegment result = new ColumnProjectionSegment((ColumnSegment) exprProjection);
            result.setAlias(alias);
            return result;
        }
        if (exprProjection instanceof SubquerySegment) {
            SubquerySegment subquerySegment = (SubquerySegment) exprProjection;
            String text = ctx.start.getInputStream().getText(new Interval(subquerySegment.getStartIndex(), subquerySegment.getStopIndex()));
            SubqueryProjectionSegment result = new SubqueryProjectionSegment((SubquerySegment) exprProjection, text);
            result.setAlias(alias);
            return result;
        }
        if (exprProjection instanceof ExistsSubqueryExpression) {
            ExistsSubqueryExpression existsSubqueryExpression = (ExistsSubqueryExpression) exprProjection;
            String text = ctx.start.getInputStream().getText(new Interval(existsSubqueryExpression.getStartIndex(), existsSubqueryExpression.getStopIndex()));
            SubqueryProjectionSegment result = new SubqueryProjectionSegment(((ExistsSubqueryExpression) exprProjection).getSubquery(), text);
            result.setAlias(alias);
            return result;
        }
        return createProjection(ctx, alias, exprProjection);
    }
    
    private ShorthandProjectionSegment createShorthandProjection(final QualifiedShorthandContext shorthand) {
        ShorthandProjectionSegment result = new ShorthandProjectionSegment(shorthand.getStart().getStartIndex(), shorthand.getStop().getStopIndex());
        IdentifierContext identifier = shorthand.identifier().get(shorthand.identifier().size() - 1);
        OwnerSegment owner = new OwnerSegment(identifier.getStart().getStartIndex(), identifier.getStop().getStopIndex(), new IdentifierValue(identifier.getText()));
        result.setOwner(owner);
        if (shorthand.identifier().size() > 1) {
            IdentifierContext schemaIdentifier = shorthand.identifier().get(0);
            owner.setOwner(new OwnerSegment(schemaIdentifier.getStart().getStartIndex(), schemaIdentifier.getStop().getStopIndex(), new IdentifierValue(schemaIdentifier.getText())));
        }
        return result;
    }
    
    @Override
    public ASTNode visitAlias(final AliasContext ctx) {
        return new AliasSegment(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), new IdentifierValue(ctx.textOrIdentifier().getText()));
    }
    
    private ASTNode createProjection(final ProjectionContext ctx, final AliasSegment alias, final ASTNode projection) {
        if (projection instanceof AggregationProjectionSegment) {
            ((AggregationProjectionSegment) projection).setAlias(alias);
            return projection;
        }
        if (projection instanceof ExpressionProjectionSegment) {
            ((ExpressionProjectionSegment) projection).setAlias(alias);
            return projection;
        }
        if (projection instanceof FunctionSegment) {
            FunctionSegment functionSegment = (FunctionSegment) projection;
            ExpressionProjectionSegment result = new ExpressionProjectionSegment(functionSegment.getStartIndex(), functionSegment.getStopIndex(), functionSegment.getText(), functionSegment);
            result.setAlias(alias);
            return result;
        }
        if (projection instanceof CommonExpressionSegment) {
            CommonExpressionSegment segment = (CommonExpressionSegment) projection;
            ExpressionProjectionSegment result = new ExpressionProjectionSegment(segment.getStartIndex(), segment.getStopIndex(), segment.getText(), segment);
            result.setAlias(alias);
            return result;
        }
        // FIXME :For DISTINCT()
        if (projection instanceof ColumnSegment) {
            ExpressionProjectionSegment result = new ExpressionProjectionSegment(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), getOriginalText(ctx), (ColumnSegment) projection);
            result.setAlias(alias);
            return result;
        }
        if (projection instanceof SubqueryExpressionSegment) {
            SubqueryExpressionSegment subqueryExpressionSegment = (SubqueryExpressionSegment) projection;
            String text = ctx.start.getInputStream().getText(new Interval(subqueryExpressionSegment.getStartIndex(), subqueryExpressionSegment.getStopIndex()));
            SubqueryProjectionSegment result = new SubqueryProjectionSegment(subqueryExpressionSegment.getSubquery(), text);
            result.setAlias(alias);
            return result;
        }
        if (projection instanceof BinaryOperationExpression) {
            int startIndex = ((BinaryOperationExpression) projection).getStartIndex();
            int stopIndex = null != alias ? alias.getStopIndex() : ((BinaryOperationExpression) projection).getStopIndex();
            ExpressionProjectionSegment result = new ExpressionProjectionSegment(startIndex, stopIndex, ((BinaryOperationExpression) projection).getText(), (BinaryOperationExpression) projection);
            result.setAlias(alias);
            return result;
        }
        if (projection instanceof ParameterMarkerExpressionSegment) {
            ParameterMarkerExpressionSegment result = (ParameterMarkerExpressionSegment) projection;
            result.setAlias(alias);
            return projection;
        }
        if (projection instanceof CaseWhenExpression) {
            ExpressionProjectionSegment result = new ExpressionProjectionSegment(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), getOriginalText(ctx.expr()), (CaseWhenExpression) projection);
            result.setAlias(alias);
            return result;
        }
        LiteralExpressionSegment column = (LiteralExpressionSegment) projection;
        ExpressionProjectionSegment result = null == alias
                ? new ExpressionProjectionSegment(column.getStartIndex(), column.getStopIndex(), String.valueOf(column.getLiterals()), column)
                : new ExpressionProjectionSegment(column.getStartIndex(), ctx.alias().stop.getStopIndex(), String.valueOf(column.getLiterals()), column);
        result.setAlias(alias);
        return result;
    }
    
    @Override
    public ASTNode visitFromClause(final FromClauseContext ctx) {
        return visit(ctx.tableReferences());
    }

    /**
     * 表对象访问
     * @param ctx
     * @return
     */
    @Override
    public ASTNode visitTableReferences(final TableReferencesContext ctx) {
        // 获取第一个表部分
        TableSegment result = (TableSegment) visit(ctx.tableReference(0));
        if (ctx.tableReference().size() > 1) {
            // 连接表的访问
            for (int i = 1; i < ctx.tableReference().size(); i++) {
                result = generateJoinTableSourceFromEscapedTableReference(ctx.tableReference(i), result);
            }
        }
        return result;
    }

    /**
     * 连接表部分访问
     * @param ctx
     * @param tableSegment
     * @return
     */
    private JoinTableSegment generateJoinTableSourceFromEscapedTableReference(final TableReferenceContext ctx, final TableSegment tableSegment) {
        JoinTableSegment result = new JoinTableSegment();
        result.setStartIndex(tableSegment.getStartIndex());
        result.setStopIndex(ctx.stop.getStopIndex());
        result.setLeft(tableSegment);
        result.setJoinType(JoinType.COMMA.name());
        result.setRight((TableSegment) visit(ctx));
        return result;
    }

    /**
     * 访问表部分
     * @param ctx
     * @return
     */
    @Override
    public ASTNode visitTableReference(final TableReferenceContext ctx) {
        TableSegment result;
        TableSegment left;
        // 访问TableFactor部分
        left = null != ctx.tableFactor() ? (TableSegment) visit(ctx.tableFactor()) : (TableSegment) visit(ctx.escapedTableReference());
        // 如果有连接表
        for (JoinedTableContext each : ctx.joinedTable()) {
            // 访问连接表部分
            left = visitJoinedTable(each, left);
        }
        result = left;
        return result;
    }

    /**
     * 访问表因素
     * @param ctx
     * @return
     */
    @Override
    public ASTNode visitTableFactor(final TableFactorContext ctx) {
        // 子查询访问
        if (null != ctx.subquery()) {
            MySQLSelectStatement subquery = (MySQLSelectStatement) visit(ctx.subquery());
            SubquerySegment subquerySegment = new SubquerySegment(ctx.subquery().start.getStartIndex(), ctx.subquery().stop.getStopIndex(), subquery);
            SubqueryTableSegment result = new SubqueryTableSegment(subquerySegment);
            if (null != ctx.alias()) {
                result.setAlias((AliasSegment) visit(ctx.alias()));
            }
            return result;
        }
        // 表名访问
        if (null != ctx.tableName()) {
            // 访问表名部分,获取简单表部分对象
            SimpleTableSegment result = (SimpleTableSegment) visit(ctx.tableName());
            // 如果有昵称
            if (null != ctx.alias()) {
                // 访问昵称部分
                result.setAlias((AliasSegment) visit(ctx.alias()));
            }
            return result;
        }
        return visit(ctx.tableReferences());
    }

    /**
     * 访问where部分
     * @param ctx
     * @return
     */
    @Override
    public ASTNode visitWhereClause(final WhereClauseContext ctx) {
        // 访问表达式
        ASTNode segment = visit(ctx.expr());
        // 创建where部分
        return new WhereSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), (ExpressionSegment) segment);
    }

    // 省略其他
    
}

示例解析

还是下面的sql为例:

select id, name from tb_user where id = 1

通过Antlr的访问者(Visitor)接口遍历语法树时,对于以上的select语句,会先访问MySQLStatementBaseVisitor的visitSelect() -> visitQueryExpression() -> visitQueryExpressionBody() -> visitQueryPrimary() -> visitQuerySpecification()。

MySQLStatementSQLVisitor重写了visitQuerySpecification()方法,进而逐步访问匹配的投影、from部分、where部分。执行步骤如下:

解析之后,生成一个MySQLSelectStatement对象。

其他操作类型的sql语句的解析流程也大体相当。

小结

限于篇幅,本篇先分享到这里。以下做一个小结:

1)Sharding-JDBC支持的数据库包括:mysql、opengauss、oracle、postgresql、sql92、sqlserver;

2)Sharding-JDBC采用 Antlr 解析 sql 语句,不同的数据库对应不同的 .g4 文件;

文件路径在源码的 sql-parser/dialect 目录下;

文件名称为 XxxStatement.g4,其中Xxx对应数据库名;

3)对于 MySQL 数据库,规则文件为 MySQLStatement.g4,常用的增删改查的规则信息定义在 DMLStatement.g4文件;

3.1)通过 Antlr 的 Generate ANTLR Recognizer,自动生成 MySQLStatementBaseVisitor 文件。Sharding-JDBC自定义MySQLStatementSQLVisitor,该类继承 MySQLStatementBaseVisitor;

3.2)对于 select 查询,通过 Antlr 的访问者(Visitor)接口访问生成树时,入口方法为MySQLStatementBaseVisitor的visitSelect()方法。解析后生成MySQLSelectStatement对象;

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

相关推荐
JingAi_jia9176 小时前
【源码】Sharding-JDBC源码分析之SQL路由及SingleSQLRouter单表路由
分库分表·分片路由·springboot分库分表·shardingjdbc源码·spring分库分表·singlesqlrouter·单表路由
惜.己9 小时前
Jmeter中的断言(二)
测试工具·jmeter·1024程序员节
西电研梦18 小时前
考研倒计时30天丨和西电一起向前!再向前!
人工智能·考研·1024程序员节·西电·西安电子科技大学
惜.己19 小时前
Jmeter中的断言(四)
测试工具·jmeter·1024程序员节
·云扬·1 天前
Java IO 与 BIO、NIO、AIO 详解
java·开发语言·笔记·学习·nio·1024程序员节
网安_秋刀鱼1 天前
PHP代码审计 --MVC模型开发框架&rce示例
开发语言·web安全·网络安全·php·mvc·1024程序员节
HUODUNYUN2 天前
小程序免备案:快速部署与优化的全攻略
服务器·网络·web安全·小程序·1024程序员节
惜.己2 天前
Jmeter的后置处理器(二)
测试工具·github·1024程序员节
惜.己2 天前
Jmeter中的断言(一)
测试工具·jmeter·1024程序员节
cainiao0806052 天前
《物理学进展》
1024程序员节·核心期刊·知网期刊·职称评审