ShardingJdbc2.X學習總結系列(十二):源碼解析—SQL解析之表名解析

這篇文章我們會詳細看一下SQL裏面表名解析的詳細過程。不管是增刪改查最終表結構的解析都是走TableReferencesClauseParser

這個解析類,我們看一下這個類的結構圖

我們看一下這個parse方法的整體調用流程

public final void parse(final SQLStatement sqlStatement, final boolean isSingleTableOnly) {
        do {
            parseTableReference(sqlStatement, isSingleTableOnly);
        } while (lexerEngine.skipIfEqual(Symbol.COMMA));
    }
    
    protected void parseTableReference(final SQLStatement sqlStatement, final boolean isSingleTableOnly) {
        parseTableFactor(sqlStatement, isSingleTableOnly);
    }

真正解析的方法其實是parseTableFactor,我們詳細看一下

protected final void parseTableFactor(final SQLStatement sqlStatement, final boolean isSingleTableOnly) {
        final int beginPosition = lexerEngine.getCurrentToken().getEndPosition() - lexerEngine.getCurrentToken().getLiterals().length();
        //獲取當前的表名稱字段
        String literals = lexerEngine.getCurrentToken().getLiterals();
        lexerEngine.nextToken();
        if (lexerEngine.equalAny(Symbol.DOT)) {
            throw new UnsupportedOperationException("Cannot support SQL for `schema.table`");
        }
        String tableName = SQLUtil.getExactlyValue(literals);
        if (Strings.isNullOrEmpty(tableName)) {
            return;
        }
        //解析別名
        Optional<String> alias = aliasExpressionParser.parseTableAlias();
        //如果是單表或者是找到了當前表在分庫分表配置中或者默認的數據源在配置的數據源中
        if (isSingleTableOnly || shardingRule.tryFindTableRule(tableName).isPresent() || shardingRule.findBindingTableRule(tableName).isPresent()
                || shardingRule.getDataSourceMap().containsKey(shardingRule.getDefaultDataSourceName())) {
            //這裏添加一個TableToken
            sqlStatement.getSqlTokens().add(new TableToken(beginPosition, literals));
            //這裏添加一個Table
            sqlStatement.getTables().add(new Table(tableName, alias));
        }
        //解析join
        parseJoinTable(sqlStatement);
        if (isSingleTableOnly && !sqlStatement.getTables().isSingleTable()) {
            throw new UnsupportedOperationException("Cannot support Multiple-Table.");
        }
    }

我們看下解析join語句(LEFT, RIGHT,FULL,INNER,OUTER),主要是解析表名信息+on關係

private void parseJoinTable(final SQLStatement sqlStatement) {
        //這裏判斷是不是join語句的關鍵詞(LEFT, RIGHT,FULL,INNER,OUTER)
        while (parseJoinType()) {
            if (lexerEngine.equalAny(Symbol.LEFT_PAREN)) {
                throw new UnsupportedOperationException("Cannot support sub query for join table.");
            }
            //解析表名信息
            parseTableFactor(sqlStatement, false);
            //解析join的關聯關係
            parseJoinCondition(sqlStatement);
        }
    }

下面看一下join  On 關係的解析

private void parseJoinCondition(final SQLStatement sqlStatement) {
        //如果是ON關鍵字,跳過並解析後續條件
        if (lexerEngine.skipIfEqual(DefaultKeyword.ON)) {
            do {
                //解析條件左邊
                basicExpressionParser.parse(sqlStatement);
                lexerEngine.accept(Symbol.EQ);
                //解析條件右邊
                basicExpressionParser.parse(sqlStatement);
            } while (lexerEngine.skipIfEqual(DefaultKeyword.AND));
        } else if (lexerEngine.skipIfEqual(DefaultKeyword.USING)) {
            lexerEngine.skipParentheses(sqlStatement);
        }
    }

下面我們接着看,如何解析條件關係的

public SQLExpression parse(final SQLStatement sqlStatement) {
        int beginPosition = lexerEngine.getCurrentToken().getEndPosition();
        //解析SQL關係
        SQLExpression result = parseExpression(sqlStatement);
        //如果條件是屬性條件的話  設置TableToken
        if (result instanceof SQLPropertyExpression) {
            setTableToken(sqlStatement, beginPosition, (SQLPropertyExpression) result);
        }
        return result;
    }

繼續往下走,我們看下SQL關係的解析

private SQLExpression parseExpression(final SQLStatement sqlStatement) {
        String literals = lexerEngine.getCurrentToken().getLiterals();
        final int beginPosition = lexerEngine.getCurrentToken().getEndPosition() - literals.length();
        //獲取條件信息
        final SQLExpression expression = getExpression(literals, sqlStatement);
        lexerEngine.nextToken();
        if (lexerEngine.skipIfEqual(Symbol.DOT)) {
            String property = lexerEngine.getCurrentToken().getLiterals();
            lexerEngine.nextToken();
            return skipIfCompositeExpression(sqlStatement)
                    ? new SQLIgnoreExpression(lexerEngine.getInput().substring(beginPosition, lexerEngine.getCurrentToken().getEndPosition()))
                    : new SQLPropertyExpression(new SQLIdentifierExpression(literals), property);
        }
        if (lexerEngine.equalAny(Symbol.LEFT_PAREN)) {
            lexerEngine.skipParentheses(sqlStatement);
            skipRestCompositeExpression(sqlStatement);
            return new SQLIgnoreExpression(lexerEngine.getInput().substring(beginPosition,
                    lexerEngine.getCurrentToken().getEndPosition() - lexerEngine.getCurrentToken().getLiterals().length()).trim());
        }
        return skipIfCompositeExpression(sqlStatement)
                ? new SQLIgnoreExpression(lexerEngine.getInput().substring(beginPosition, lexerEngine.getCurrentToken().getEndPosition())) : expression;
    }

我們看下真正的表達式解析

private SQLExpression getExpression(final String literals, final SQLStatement sqlStatement) {
        //如果是? 解析
        if (lexerEngine.equalAny(Symbol.QUESTION)) {
            sqlStatement.increaseParametersIndex();
            return new SQLPlaceholderExpression(sqlStatement.getParametersIndex() - 1);
        }
        //如果是字符
        if (lexerEngine.equalAny(Literals.CHARS)) {
            return new SQLTextExpression(literals);
        }
        //如果是整形數值
        if (lexerEngine.equalAny(Literals.INT)) {
            return new SQLNumberExpression(NumberUtil.getExactlyNumber(literals, 10));
        }
        //如果是浮點數值
        if (lexerEngine.equalAny(Literals.FLOAT)) {
            return new SQLNumberExpression(Double.parseDouble(literals));
        }
        //如果是16進制
        if (lexerEngine.equalAny(Literals.HEX)) {
            return new SQLNumberExpression(NumberUtil.getExactlyNumber(literals, 16));
        }
        //如果是SQL 關鍵字
        if (lexerEngine.equalAny(Literals.IDENTIFIER)) {
            return new SQLIdentifierExpression(SQLUtil.getExactlyValue(literals));
        }
        //其他處理
        return new SQLIgnoreExpression(literals);
    }

到這裏表名解析就結束了,我們看下解析出的數據格式主要是填充下面3中類型

Tables getTables();
Conditions getConditions();
List<SQLToken> getSqlTokens();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章