上一篇文章咱们分析了SQL解析中会涉及到的一些类,以及用到的一些方法,今天咱们分析Select语句,来看看sharding-jdbc是如何解析的。sql
SELECT o.order_id FROM order o WHERE o.user_id = XXXX数据库
咱们以上面的查询语句来分析。express
public SQLStatement parse() {
//一、获取SQLParser
SQLParser sqlParser = getSQLParser();
sqlParser.skipIfEqual(Symbol.SEMI);
if (sqlParser.equalAny(DefaultKeyword.WITH)) {
skipWith(sqlParser);
}
//二、根据不一样的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());
}
复制代码
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());
}
}
复制代码
/**
* 判断当前词法标记类型是否与其中一个传入值相等.
*
* @param tokenTypes 待判断的词法标记类型
* @return 是否有相等的词法标记类型
*/
public final boolean equalAny(final TokenType... tokenTypes) {
for (TokenType each : tokenTypes) {
if (each == lexer.getCurrentToken().getType()) {
return true;
}
}
return false;
}
复制代码
/**
* 建立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()));
}
复制代码
public final SelectStatement parse() {
//解析表名称、查询条件
query();
//解析order by
parseOrderBy();
customizedSelect();
appendDerivedColumns();
appendDerivedOrderBy();
return selectStatement;
}
复制代码
@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强相关的方法,其余的方法差很少的,相信本身分析会更有收获😊编程
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填充完毕.bash
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<String> 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标记app
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-一、解析SQL表达式,主要有下面几种:less
3-二、判断条件,由于是“=” 因此走的是相等条件ide
public final void parseComparisonCondition(final SQLStatement sqlStatement) {
skipIfEqual(Symbol.LEFT_PAREN);
//一、SQL表达式
SQLExpression left = parseExpression(sqlStatement);
//二、判断条件
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-三、解析“=”右边的表达式,右边是*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> column = find(sqlStatement.getTables(), left);
//添加condition
if (column.isPresent()) {
sqlStatement.getConditions().add(new Condition(column.get(), right), shardingRule);
}
}
}
复制代码
3-四、只有是分片列才能被添加到conditionpost
/**
* 添加条件对象.
*
* @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的继承关系图:点我查看大图
小尾巴走一波,欢迎关注个人公众号,不按期分享编程、投资、生活方面的感悟:)