前言
上一篇文章我们分析了中会涉及到的一些类,以及用到的一些方法,今天我们分析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()); }复制代码
- 获取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()); } }复制代码
- 第一个单词为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; }复制代码
- 入参为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强相关的方法,其他的方法差不多的,相信自己分析会更有收获?
- 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> items 、sqlTokens填充完毕.
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 OptionalparseAlias() { 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表达式,主要有下面几种:
我们这里“=”左边的是 SQLPropertyExpression3-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 Optionalcolumn = 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的继承关系图:
最后:
小尾巴走一波,欢迎关注我的公众号,不定期分享编程、投资、生活方面的感悟:)