【源码】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的部分源码如下:

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的部分源码如下:

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的部分源码如下:

// 表达式
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源码

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对象;

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/904618.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【ECMAScript标准】深入解析ES5:现代JavaScript的基石

&#x1f9d1;‍&#x1f4bc; 一名茫茫大海中沉浮的小小程序员&#x1f36c; &#x1f449; 你的一键四连 (关注 点赞收藏评论)是我更新的最大动力❤️&#xff01; &#x1f4d1; 目录 &#x1f53d; 前言1️⃣ ES5的概述2️⃣ ES5的关键特性3️⃣ ES5与之前版本的区别4️⃣ …

Unity(四十八):Unity与Web双向交互

效果 游戏对象绑定脚本 游戏脚本源码 using System.Collections; using System.Collections.Generic; using UnityEngine;public class Tent : MonoBehaviour {public Camera camera;// Start is called before the first frame updatevoid Start(){}// Update is called once…

CloudSat数据产品数据下载与处理 (matlab)

CloudSat数据下载 这个数据我之前和CALIPSO弄混了&#xff0c;后来发现它们虽然是同一个火箭上去&#xff0c;但是数据产品却在不同的平台下&#xff0c;CloudSat的数据更加关注云的特性&#xff0c;包括云覆盖、云水当量、云分类数据。 数据网址在&#xff1a;CloudSat网址 …

端侧大模型浪潮奔涌而至:态势、影响与建议

腾讯研究院大模型研究小分队出品 自苹果推出AI手机以来&#xff0c;端侧大模型的产品发布进入加速期。 10月10日&#xff0c;Vivo推出蓝心端侧大模型 3B&#xff0c;其AI能力已覆盖60多个国家和地区&#xff0c;服务超5亿手机用户&#xff0c;大模型token输出量超过3万亿&…

【jvm】为什么Xms和Xmx的值通常设置为相同的?

目录 1. 说明2. 避免性能开销3. 提升稳定性4. 简化配置5. 优化垃圾收集6. 获取参数6.1 代码示例6.2 结果示例 1. 说明 1.-Xms 和 -Xmx 参数分别用于设置堆内存的初始大小&#xff08;最小值&#xff09;和最大大小。2.在开发环境中&#xff0c;开发人员可能希望快速启动应用程…

Docker搭建官方私有仓库registry及相关配置

&#xff08;企业不推荐使用registry&#xff09; 在 Docker 中&#xff0c;当我们执行 docker pull xxx 的时候 &#xff0c;它实际上是从 https://registry.hub.docker.com/ 这个地址去查找&#xff0c;这就是Docker公司为我们提供的公共仓库。 在工作中&#xff0c;我们不可…

Rust编程中的浮点数比较

缘由&#xff1a;在看Rust编写的代码&#xff0c;发现了一行浮点数等于比较的代码&#xff0c;于是编辑如下内容。 在Rust中&#xff0c;进行浮点数比较时需要特别小心&#xff0c;因为浮点数由于精度限制无法精确表示小数&#xff0c;可能会导致直接比较&#xff08;如 &…

获取Hive表备注

DESCRIBE EXTENDED 表名;先获取Detailed Table Information这行的data_type字段数据&#xff0c;进行正则匹配&#xff0c;拿到表备注&#xff0c;如下&#xff1a; String str ReUtil.get("parameters:\\{(?!.*?\\().*transient_lastDdlTime.*?comment(.*?)\\}&quo…

【笔记】记一次因Spring版本和Tomcat版本不对应,造成Spring MVC项目启动后页面访问报404的问题

项目结构 pom.xml文件内容 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apa…

大模型,多模态大模型面试【LoRA,分类,动静态数据类型,DDPM,ControlNet,IP-Adapter, Stable Diffusion】

大模型&#xff0c;多模态大模型面试【LoRA&#xff0c;分类&#xff0c;动静态数据类型&#xff0c;DDPM&#xff0c;ControlNet&#xff0c;IP-Adapter, Stable Diffusion】 问题一&#xff1a;LoRA是用在节省资源的场景下&#xff0c;那么LoRA具体是节省了内存带宽还是显存呢…

BGP路由优选+EVPN

BGP 的路由优选规则是一套多步决策链&#xff0c;用来确定在多个可行路由中选择最优的路由。BGP 是一种路径向量协议&#xff0c;通过这些优选规则&#xff0c;网络管理员可以控制数据流量的流向&#xff0c;确保网络的稳定性和效率。下面以一个实例来详细说明 BGP 的优选规则及…

大桥局老施工员的肺腑之言:桥梁安全,为何离不开自动化监测?

随着我国桥梁数量的快速增长&#xff0c;同时既有的许多桥梁逐渐进入了养护维修阶段。有关专家认为桥梁使用超过25年以上则进入老化期&#xff1b;据统计&#xff0c;我国桥梁总数的40%已经属于“老龄”桥梁。而且随着时间的推移&#xff0c;其数量还在不断增长&#xff0c;屡屡…

项目符合行业安全标准的必要步骤与实用建议

要保障项目符合行业安全标准&#xff0c;关键在于建立全面的安全管理体系、定期进行风险评估、持续培训员工&#xff0c;以及确保合规性文件和审核流程完整。例如&#xff0c;通过建立合规文件和审核流程&#xff0c;可以系统性地跟踪项目的安全实践和合规性&#xff0c;使安全…

openpnp - 手工修改配置文件(元件高度,size,吸嘴)

文章目录 openpnp - 手工修改配置文件(元件高度,size,吸嘴)概述笔记parts.xmlpackages.xml 手工将已经存在的NT1,NT2拷贝出来改名备注END openpnp - 手工修改配置文件(元件高度,size,吸嘴) 概述 载入新板子贴片准备时&#xff0c;除了引入Named CSV文件&#xff0c;还要在ope…

Fakelocation 运动世界校园(虚拟机篇)

前言:需要一个Root虚拟机&#xff0c;Fakelocation&#xff0c;运动世界校园&#xff0c;Dia系统要求 虚拟机 | Fakelocation | MT管理器 | Dia 任务一 虚拟机&#xff08;内置root完整德尔塔环境&#xff09; 请设置一个路线模拟&#xff0c;并且定位成功&#xff0c;确保f…

redis实现分布式锁,go实现完整code

Redis分布式锁 Redis 分布式锁是一种使用 Redis 数据库实现分布式锁的方式&#xff0c;可以保证在分布式环境中同一时间只有一个实例可以访问共享资源。 实现机制 以下是实现其加锁步骤&#xff1a; 获取锁 在 Redis 中&#xff0c;一个相同的key代表一把锁。是否拥有这把锁&…

池化层笔记

池化层 文章目录 池化层二维池化层超参数池化层的分类代码实现填充和步幅 多个通道 总结 卷积对位置敏感&#xff0c;可以检测垂直边缘。需要有一定程度的平移不变性&#xff0c;而在平时图片的拍摄&#xff0c;会因为图片的照明&#xff0c;物体位置&#xff0c;比例&#xff…

React核心思维模型(一)

一、数据和视图分离&#xff0c;数据改变驱动视图更新 <div>Tom</div>如果我们想修改上述div盒子中的Tom为Jerry&#xff0c;应该怎样修改呢 在jquery中我们直接把界面元素抓过来修改 document.getElementsByTagName(div).item(0) Jerry 但在react中&#xf…

MoveIt 控制自己的真实机械臂【2】——编写 action server 端代码

完成了 MoveIt 这边 action client 的基本配置&#xff0c;MoveIt 理论上可以将规划好的 trajectory 以 action 的形式发布出来了&#xff0c;浅浅尝试一下&#xff0c;在 terminal 中运行 roslaunch xmate7_moveit_config_new demo.launch 报错提示他在等待 xmate_arm_control…

jenkins部署手册

文章目录 一、环境配置资源配置操作系统资源配置服务器 二、jenkins软件部署2.1 下载软件包2.2 启动jenkins2.2.1 准备jdk环境2.2.2 准备maven环境2.2.3 编写jenkins.service 2.3 配置jenkins2.3.1 修改插件源&#xff08;非必要不修改&#xff09;2.3.2 配置环境变量2.3.3 配置…