首页 > 数据库 >【源码】Sharding-JDBC源码分析之Sql解析的原理

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

时间:2024-10-25 08:48:51浏览次数:3  
标签:JDBC return visit ctx 源码 result Sql new null

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

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

标签:JDBC,return,visit,ctx,源码,result,Sql,new,null
From: https://blog.csdn.net/JingAi_jia917/article/details/143017847

相关文章

  • 收银系统源码如何选择?
    提到收银系统源码,想必很多企业都为此头疼,收银系统厂商五花八门,价格从几千元,到几十万元,甚至上百万元不等。到底如何选择一套适合自己的收银系统源码呢?本文我将结合自己8年从业经验展推荐大家从以下几个方面去评估,希望可以帮到大家!1.为什么要入手开源版的收银系统?首先要先清楚......
  • SQLite 数据库如何多行数据连接成字符串
    在使用SQLite数据库进行数据处理时,常常会遇到需要将多行数据连接成一个字符串的需求。举例来说,假设我们有一个存储用户名的表,想将这些用户名用逗号分隔组合成一个字符串,以便展示或进一步处理。SQLite本身不像某些其他数据库系统(如MySQL、PostgreSQL)那样原生支持直接的字符串聚......
  • 基于SpringBoot+Vue+uniapp的乡村政务办公系统的详细设计和实现(源码+lw+部署文档+讲
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 在 Mac 上有什么好的打开 .sqlite 或者 .db 数据库文件的软件
    在Mac上好的打开.sqlite或者.db数据库文件的软件有:1.DBBrowserforSQLite;2.SQLiteStudio;3.Base;4.PawSQLitePawset;5.NavicatforSQLite;6.DBeaver。DBBrowserforSQLite是一款免费、开源的SQLite数据库浏览器,适用于Mac、Windows和Linux系统。在Mac上打开.sql......
  • 织梦重置mysql数据库密码忘记_织梦cms管理员密码忘记了怎么重置找回?
    方法一:通过phpMyAdmin登录数据库,在dede_admin表中重置密码登录数据库管理界面登录网站所在的虚拟主机或服务器,进入数据库管理(phpMyAdmin)界面。选择网站所用的MySQL数据库。找到并浏览dede_admin表在数据库中找到dede_admin表。点击表名,选择“浏览”选项,进入表数据......
  • ClickHouse与MySQL对比
    总结:❓Prompt将以上我们两天内容整理并总结.可以用表格的用表格.能用画图说明的就用画图.总之就是用最好的方式呈现即可......
  • JDBC学习
    JDBC核心组件DriverManager:管理数据库驱动程序,处理数据库连接的创建。Connection:代表与数据库的连接会话。Statement:用于执行静态SQL语句并返回结果。PreparedStatement:预编译的SQL语句,可以执行动态SQL并提高性能。CallableStatement:用于调用数据库中的存储......
  • [MySQL#1] database概述 | 常见的操作指令 | MySQL架构 | 存储引擎
    #1024程序员节|征文#目录一.数据库概念0.连接服务器1.什么是数据库口语中的数据库为什么数据不直接以文件形式存储,而需要使用数据库呢?总结二.......
  • sql数据库查询语句
    ###标题:SQL数据库查询语句在探讨SQL数据库查询语句时,我们首先要明确,SQL(结构化查询语言)是用于存取、修改、删除以及查询数据库中数据的标准编程语言。核心观点包括数据检索、数据操作、数据定义、数据控制等方面。在这些方面中,数据检索是最为常用和关键的一环,它使得用户能够根据......
  • [Flink SQL] FlinkCdcSqlJob启动时因MYSQL serverTimeZone而报错:`The MySQL server ha
    1问题描述FlinkCdcSqlJob启动时报错...Causedby:org.apache.flink.table.api.ValidationException:TheMySQLserverhasatimezoneoffset(0secondsaheadofUTC)whichdoesnotmatchtheconfiguredtimezoneAsia/Shanghai.Specifytherightserver-time-z......