博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Shading - jdbc 源码分析(三) - sql 解析之 Select
阅读量:5883 次
发布时间:2019-06-19

本文共 16350 字,大约阅读时间需要 54 分钟。

前言

上一篇文章我们分析了中会涉及到的一些类,以及用到的一些方法,今天我们分析Select语句,来看看sharding-jdbc是如何解析的。

SELECT o.order_id FROM order o WHERE o.user_id = XXXX

我们以上面的查询语句来分析。

SQLParsingEngine#parse() 解析入口:

public SQLStatement parse() {        //1、获取SQLParser        SQLParser sqlParser = getSQLParser();        sqlParser.skipIfEqual(Symbol.SEMI);        if (sqlParser.equalAny(DefaultKeyword.WITH)) {            skipWith(sqlParser);        }        //2、根据不同的SQL 实例化相应的SQLStatementParser,并调用parse()方法;        if (sqlParser.equalAny(DefaultKeyword.SELECT)) {            return SelectParserFactory.newInstance(sqlParser).parse();        }        if (sqlParser.equalAny(DefaultKeyword.INSERT)) {            return InsertParserFactory.newInstance(shardingRule, sqlParser).parse();        }        if (sqlParser.equalAny(DefaultKeyword.UPDATE)) {            return UpdateParserFactory.newInstance(sqlParser).parse();        }        if (sqlParser.equalAny(DefaultKeyword.DELETE)) {            return DeleteParserFactory.newInstance(sqlParser).parse();        }        throw new SQLParsingUnsupportedException(sqlParser.getLexer().getCurrentToken().getType());    }复制代码
  1. 获取SQLParser,本文以MySQL为例,返回MySQLParser;
private SQLParser getSQLParser() {        switch (dbType) {            case H2:            case MySQL:                return new MySQLParser(sql, shardingRule);            case Oracle:                return new OracleParser(sql, shardingRule);            case SQLServer:                return new SQLServerParser(sql, shardingRule);            case PostgreSQL:                return new PostgreSQLParser(sql, shardingRule);            default:                throw new UnsupportedOperationException(dbType.name());        }    }复制代码
  1. 第一个单词为SELECT,所以走SelectParserFactory.newInstance(sqlParser)
/**     * 判断当前词法标记类型是否与其中一个传入值相等.     *     * @param tokenTypes 待判断的词法标记类型     * @return 是否有相等的词法标记类型     */    public final boolean equalAny(final TokenType... tokenTypes) {        for (TokenType each : tokenTypes) {            if (each == lexer.getCurrentToken().getType()) {                return true;            }        }        return false;    }复制代码
  1. 入参为MySQLParser,此处返回MySQLSelectParser,最终调用MySQLSelectParser的parse()方法
/**     * 创建Select语句解析器.     *      * @param sqlParser SQL解析器     * @return Select语句解析器     */    public static AbstractSelectParser newInstance(final SQLParser sqlParser) {        if (sqlParser instanceof MySQLParser) {            return new MySQLSelectParser(sqlParser);        }        if (sqlParser instanceof OracleParser) {            return new OracleSelectParser(sqlParser);        }        if (sqlParser instanceof SQLServerParser) {            return new SQLServerSelectParser(sqlParser);        }        if (sqlParser instanceof PostgreSQLParser) {            return new PostgreSQLSelectParser(sqlParser);        }        throw new UnsupportedOperationException(String.format("Cannot support sqlParser class [%s].", sqlParser.getClass()));    } 复制代码

MySQLSelectParser#parse()方法

public final SelectStatement parse() {       //解析表名称、查询条件        query();        //解析order by        parseOrderBy();        customizedSelect();        appendDerivedColumns();        appendDerivedOrderBy();        return selectStatement;    }复制代码

query():

@Override    public void query() {        if (getSqlParser().equalAny(DefaultKeyword.SELECT)) {            getSqlParser().getLexer().nextToken();            //解析distinct            parseDistinct();            //跳过一些词            getSqlParser().skipAll(MySQLKeyword.HIGH_PRIORITY, DefaultKeyword.STRAIGHT_JOIN, MySQLKeyword.SQL_SMALL_RESULT, MySQLKeyword.SQL_BIG_RESULT, MySQLKeyword.SQL_BUFFER_RESULT,                    MySQLKeyword.SQL_CACHE, MySQLKeyword.SQL_NO_CACHE, MySQLKeyword.SQL_CALC_FOUND_ROWS);            //解析select items            parseSelectList();            //跳到from            skipToFrom();        }        //解析表        parseFrom();        //解析where条件        parseWhere();        //解析group by        parseGroupBy();        //解析 order by         parseOrderBy();        //解析 limit        parseLimit();        if (getSqlParser().equalAny(DefaultKeyword.PROCEDURE)) {            throw new SQLParsingUnsupportedException(getSqlParser().getLexer().getCurrentToken().getType());        }        queryRest();    }复制代码

由于篇幅有限,只分析parseSelectList、parseFrom、parseWhere、parseOrderBy 这几个跟文初的sql强相关的方法,其他的方法差不多的,相信自己分析会更有收获?

  1. parseSelectList():
protected final void parseSelectList() {        do {            //解析SelectItem            parseSelectItem();            //循环条件:当前词法为“,”        } while (sqlParser.skipIfEqual(Symbol.COMMA));        //设置select最后结束的位置        selectStatement.setSelectListLastPosition(sqlParser.getLexer().getCurrentToken().getEndPosition() - sqlParser.getLexer().getCurrentToken().getLiterals().length());    }复制代码
private void parseSelectItem() {        //判断当前是否是“ROW_NUMBER”,false        if (isRowNumberSelectItem()) {            selectStatement.getItems().add(parseRowNumberSelectItem());            return;        }        sqlParser.skipIfEqual(DefaultKeyword.CONNECT_BY_ROOT);        String literals = sqlParser.getLexer().getCurrentToken().getLiterals();        //是否是Select *  false        if (isStarSelectItem(literals)) {            selectStatement.getItems().add(parseStarSelectItem());            return;        }        //是否是聚合函数(Max、Sum etc)  false        if (isAggregationSelectItem()) {            selectStatement.getItems().add(parseAggregationSelectItem(literals));            return;        }        StringBuilder expression = new StringBuilder();        Token lastToken = null;        //循环条件:select后,from前的这部分每个单词        while (!sqlParser.equalAny(DefaultKeyword.AS) && !sqlParser.equalAny(Symbol.COMMA) && !sqlParser.equalAny(DefaultKeyword.FROM) && !sqlParser.equalAny(Assist.END)) {            String value = sqlParser.getLexer().getCurrentToken().getLiterals();            int position = sqlParser.getLexer().getCurrentToken().getEndPosition() - value.length();            expression.append(value);            lastToken = sqlParser.getLexer().getCurrentToken();            sqlParser.getLexer().nextToken();            //若为“.”,则上一个单词为表的别名,添加表标记对象            if (sqlParser.equalAny(Symbol.DOT)) {                selectStatement.getSqlTokens().add(new TableToken(position, value));            }        }        //是否有别名判断(o.xxx) false        if (hasAlias(expression, lastToken)) {            selectStatement.getItems().add(parseSelectItemWithAlias(expression, lastToken));            return;        }        //add CommonSelectItem        selectStatement.getItems().add(new CommonSelectItem(SQLUtil.getExactlyValue(expression.toString()), sqlParser.parseAlias()));    }复制代码

这个步骤完成后,SelectStatement的List<SelectItem> itemssqlTokens填充完毕.

2. parseFrom()

public final void parseFrom() {        //跳过FROM        if (sqlParser.skipIfEqual(DefaultKeyword.FROM)) {            //解析Table            parseTable();        }    }复制代码
public void parseTable() {        //如果含有“(”符号,说明有子查询 false        if (sqlParser.skipIfEqual(Symbol.LEFT_PAREN)) {            if (!selectStatement.getTables().isEmpty()) {                throw new UnsupportedOperationException("Cannot support subquery for nested tables.");            }            selectStatement.setContainStar(false);            //跳过无用的嵌套小括号            sqlParser.skipUselessParentheses();            //对子查询解析(子查询其实就是一个完整的Select语句)            parse();            sqlParser.skipUselessParentheses();            if (!selectStatement.getTables().isEmpty()) {                return;            }        }        //解析表        parseTableFactor();        parseJoinTable();    }复制代码
protected final void parseTableFactor() {        int beginPosition = sqlParser.getLexer().getCurrentToken().getEndPosition() - sqlParser.getLexer().getCurrentToken().getLiterals().length();        //order        String literals = sqlParser.getLexer().getCurrentToken().getLiterals();        // o        sqlParser.getLexer().nextToken();        // TODO 包含Schema解析        if (sqlParser.skipIfEqual(Symbol.DOT)) {            sqlParser.getLexer().nextToken();            sqlParser.parseAlias();            return;        }        // FIXME 根据shardingRule过滤table        //添加TableToken 标记        selectStatement.getSqlTokens().add(new TableToken(beginPosition, literals));        //添加Table        selectStatement.getTables().add(new Table(SQLUtil.getExactlyValue(literals), sqlParser.parseAlias()));    }复制代码
/**     * 解析别名. 此处返回o     *     * @return 别名     */    public Optional
parseAlias() { if (skipIfEqual(DefaultKeyword.AS)) { if (equalAny(Symbol.values())) { return Optional.absent(); } String result = SQLUtil.getExactlyValue(getLexer().getCurrentToken().getLiterals()); getLexer().nextToken(); return Optional.of(result); } // o 被标记为IDENTIFIER // TODO 增加哪些数据库识别哪些关键字作为别名的配置 if (equalAny(Literals.IDENTIFIER, Literals.CHARS, DefaultKeyword.USER, DefaultKeyword.END, DefaultKeyword.CASE, DefaultKeyword.KEY, DefaultKeyword.INTERVAL, DefaultKeyword.CONSTRAINT)) { // o String result = SQLUtil.getExactlyValue(getLexer().getCurrentToken().getLiterals()); getLexer().nextToken(); return Optional.of(result); } return Optional.absent(); }复制代码

执行完parseFrom()后,新添加了Table和TableToken标记

3. parseWhere():

protected final void parseWhere() {        if (selectStatement.getTables().isEmpty()) {            return;        }        //解析查询条件        sqlParser.parseWhere(selectStatement);        parametersIndex = sqlParser.getParametersIndex();    }复制代码
/**     * 解析查询条件.     *     * @param sqlStatement SQL语句对象     */    public final void parseWhere(final SQLStatement sqlStatement) {        //解析别名        parseAlias();        //是否是where 标记        if (skipIfEqual(DefaultKeyword.WHERE)) {            parseConditions(sqlStatement);        }    }复制代码
private void parseConditions(final SQLStatement sqlStatement) {       //循环条件:每个词标记以and分隔(符合where条件的语义)        do {            parseComparisonCondition(sqlStatement);        } while (skipIfEqual(DefaultKeyword.AND));        if (equalAny(DefaultKeyword.OR)) {            throw new SQLParsingUnsupportedException(getLexer().getCurrentToken().getType());        }    }复制代码

3-1、解析SQL表达式,主要有下面几种:

我们这里“=”左边的是
SQLPropertyExpression

3-2、判断条件,因为是“=” 所以走的是相等条件

public final void parseComparisonCondition(final SQLStatement sqlStatement) {        skipIfEqual(Symbol.LEFT_PAREN);        //1、SQL表达式        SQLExpression left = parseExpression(sqlStatement);        //2、判断条件        if (equalAny(Symbol.EQ)) {           //处理相等条件的condition            parseEqualCondition(sqlStatement, left);            skipIfEqual(Symbol.RIGHT_PAREN);            return;        }        //in条件        if (equalAny(DefaultKeyword.IN)) {            parseInCondition(sqlStatement, left);            skipIfEqual(Symbol.RIGHT_PAREN);            return;        }        //between 条件        if (equalAny(DefaultKeyword.BETWEEN)) {            parseBetweenCondition(sqlStatement, left);            skipIfEqual(Symbol.RIGHT_PAREN);            return;        }        if (equalAny(Symbol.LT, Symbol.GT, Symbol.LT_EQ, Symbol.GT_EQ)) {            if (left instanceof SQLIdentifierExpression && sqlStatement instanceof SelectStatement                    && isRowNumberCondition((SelectStatement) sqlStatement, ((SQLIdentifierExpression) left).getName())) {                parseRowNumberCondition((SelectStatement) sqlStatement);            } else if (left instanceof SQLPropertyExpression && sqlStatement instanceof SelectStatement                    && isRowNumberCondition((SelectStatement) sqlStatement, ((SQLPropertyExpression) left).getName())) {                parseRowNumberCondition((SelectStatement) sqlStatement);            } else {                parseOtherCondition(sqlStatement);            }        } else if (equalAny(DefaultKeyword.LIKE)) {            parseOtherCondition(sqlStatement);        }        skipIfEqual(Symbol.RIGHT_PAREN);    }复制代码
/**     * 解析表达式.     *     * @param sqlStatement SQL语句对象     * @return 表达式     */    public final SQLExpression parseExpression(final SQLStatement sqlStatement) {        int beginPosition = getLexer().getCurrentToken().getEndPosition();        SQLExpression result = parseExpression();        if (result instanceof SQLPropertyExpression) {            setTableToken(sqlStatement, beginPosition, (SQLPropertyExpression) result);        }        return result;    }复制代码
/**     * 解析表达式.     *     * @return 表达式     */    // TODO 完善Expression解析的各种场景    public final SQLExpression parseExpression() {        String literals = getLexer().getCurrentToken().getLiterals();        //“=”号左边 返回SQLIdentifierExpression        final SQLExpression expression = getExpression(literals);        // true        if (skipIfEqual(Literals.IDENTIFIER)) {            //true            if (skipIfEqual(Symbol.DOT)) {                //user_id                String property = getLexer().getCurrentToken().getLiterals();                getLexer().nextToken();                //最终返回SQLPropertyExpression                return skipIfCompositeExpression() ? new SQLIgnoreExpression() : new SQLPropertyExpression(new SQLIdentifierExpression(literals), property);            }            if (equalAny(Symbol.LEFT_PAREN)) {                skipParentheses();                skipRestCompositeExpression();                return new SQLIgnoreExpression();            }            return skipIfCompositeExpression() ? new SQLIgnoreExpression() : expression;        }        getLexer().nextToken();        return skipIfCompositeExpression() ? new SQLIgnoreExpression() : expression;    }复制代码
private SQLExpression getExpression(final String literals) {        if (equalAny(Symbol.QUESTION)) {            increaseParametersIndex();            return new SQLPlaceholderExpression(getParametersIndex() - 1);        }        if (equalAny(Literals.CHARS)) {            return new SQLTextExpression(literals);        }        if (equalAny(Literals.INT)) {            return new SQLNumberExpression(NumberUtil.getExactlyNumber(literals, 10));        }        if (equalAny(Literals.FLOAT)) {            return new SQLNumberExpression(Double.parseDouble(literals));        }        if (equalAny(Literals.HEX)) {            return new SQLNumberExpression(NumberUtil.getExactlyNumber(literals, 16));        }        //true        if (equalAny(Literals.IDENTIFIER)) {            return new SQLIdentifierExpression(SQLUtil.getExactlyValue(literals));        }        return new SQLIgnoreExpression();    }复制代码

3-3、解析“=”右边的表达式,右边是*SQLNumberExpression

private void parseEqualCondition(final SQLStatement sqlStatement, final SQLExpression left) {        getLexer().nextToken();        //SQLNumberExpression        SQLExpression right = parseExpression(sqlStatement);        //true        // TODO 如果有多表,且找不到column是哪个表的,则不加入condition,以后需要解析binding table        if ((sqlStatement.getTables().isSingleTable() || left instanceof SQLPropertyExpression)                && (right instanceof SQLNumberExpression || right instanceof SQLTextExpression || right instanceof SQLPlaceholderExpression)) {            //处理column            Optional
column = find(sqlStatement.getTables(), left); //添加condition if (column.isPresent()) { sqlStatement.getConditions().add(new Condition(column.get(), right), shardingRule); } } }复制代码

3-4、只有是分片列才能被添加到condition

/**     * 添加条件对象.     *     * @param condition 条件对象     * @param shardingRule 分库分表规则配置对象     */    // TODO 添加condition时进行判断, 比如:如果以存在 等于操作 的condition, 而已存在包含 =符号 的相同column的condition, 则不添加现有的condition, 而且删除原有condition    public void add(final Condition condition, final ShardingRule shardingRule) {        // TODO 自关联有问题,表名可考虑使用别名对应        if (shardingRule.isShardingColumn(condition.getColumn())) {            conditions.put(condition.getColumn(), condition);        }    }复制代码

我们设置的分片column是user_id(这里模拟一下),执行完后,我们的SQL赋值结果:

"Column(name=user_id, tableName=order)" -> "Condition(column=Column(name=user_id, tableName=order), operator=EQUAL, positionValueMap={0=1000}, positionIndexMap={})"复制代码

最终解析完成的selectStatement 如下:

总结:

SQL解析就是在填充SQLStatement(SQL语句对象),把表名称、SQL标记、where 条件(分片列,条件是<、>还是=)解析出来,供路由的时候使用,下面再放一张 SQLStatement的继承关系图:

最后:

小尾巴走一波,欢迎关注我的公众号,不定期分享编程、投资、生活方面的感悟:)

转载地址:http://gztix.baihongyu.com/

你可能感兴趣的文章
Blender to XPS(blender 2.7x Internal materials)
查看>>
Ghrome - showModalDialog
查看>>
java例程练习(网络编程[简单双向通信试验])
查看>>
java 8 日期函数
查看>>
lnamp高性能架构之apache和nginx的整合
查看>>
Linux 多线程环境下 进程线程终止函数小结
查看>>
模块手动执行和模块导入执行
查看>>
进制转换
查看>>
UA池和代理池在scrapy中的应用
查看>>
form组件
查看>>
List的深度copy和浅度拷贝
查看>>
c语言 变量的存储类别以及对应的内存分配?
查看>>
CCF认证历年试题集
查看>>
九章算术卷第九 句股
查看>>
从更高点看软件开发的侧重点
查看>>
01.变量和字符编码
查看>>
遗传算法求系统组合
查看>>
股指的趋势持续研究(Hurst指数)
查看>>
前端上传图片 base64转二进制上传
查看>>
结构体
查看>>