/* * Copyright 1999-2015 dangdang.com. *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *

*/ package com.dangdang.ddframe.rdb.sharding.parsing.parser.statement.dql.select; import com.dangdang.ddframe.rdb.sharding.constant.AggregationType; import com.dangdang.ddframe.rdb.sharding.constant.OrderType; import com.dangdang.ddframe.rdb.sharding.parsing.lexer.token.Assist; import com.dangdang.ddframe.rdb.sharding.parsing.lexer.token.DefaultKeyword; import com.dangdang.ddframe.rdb.sharding.parsing.lexer.token.Keyword; import com.dangdang.ddframe.rdb.sharding.parsing.lexer.token.Literals; import com.dangdang.ddframe.rdb.sharding.parsing.lexer.token.Symbol; import com.dangdang.ddframe.rdb.sharding.parsing.lexer.token.Token; import com.dangdang.ddframe.rdb.sharding.parsing.parser.AbstractSQLParser; import com.dangdang.ddframe.rdb.sharding.parsing.parser.context.OrderItem; import com.dangdang.ddframe.rdb.sharding.parsing.parser.context.selectitem.AggregationSelectItem; import com.dangdang.ddframe.rdb.sharding.parsing.parser.context.selectitem.CommonSelectItem; import com.dangdang.ddframe.rdb.sharding.parsing.parser.context.selectitem.SelectItem; import com.dangdang.ddframe.rdb.sharding.parsing.parser.context.table.Table; import com.dangdang.ddframe.rdb.sharding.parsing.parser.exception.SQLParsingException; import com.dangdang.ddframe.rdb.sharding.parsing.parser.exception.SQLParsingUnsupportedException; import com.dangdang.ddframe.rdb.sharding.parsing.parser.expression.SQLExpression; import com.dangdang.ddframe.rdb.sharding.parsing.parser.expression.SQLIdentifierExpression; import com.dangdang.ddframe.rdb.sharding.parsing.parser.expression.SQLIgnoreExpression; import com.dangdang.ddframe.rdb.sharding.parsing.parser.expression.SQLNumberExpression; import com.dangdang.ddframe.rdb.sharding.parsing.parser.expression.SQLPropertyExpression; import com.dangdang.ddframe.rdb.sharding.parsing.parser.statement.SQLStatementParser; import com.dangdang.ddframe.rdb.sharding.parsing.parser.token.ItemsToken; import com.dangdang.ddframe.rdb.sharding.parsing.parser.token.OrderByToken; import com.dangdang.ddframe.rdb.sharding.parsing.parser.token.TableToken; import com.dangdang.ddframe.rdb.sharding.util.SQLUtil; import com.google.common.base.Optional; import com.google.common.collect.Lists; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; /** * Select语句解析器. * * @author zhangliang */ @Getter(AccessLevel.PROTECTED) public abstract class AbstractSelectParser implements SQLStatementParser { private static final String DERIVED_COUNT_ALIAS = "AVG_DERIVED_COUNT_%s"; private static final String DERIVED_SUM_ALIAS = "AVG_DERIVED_SUM_%s"; private static final String ORDER_BY_DERIVED_ALIAS = "ORDER_BY_DERIVED_%s"; private static final String GROUP_BY_DERIVED_ALIAS = "GROUP_BY_DERIVED_%s"; private final AbstractSQLParser sqlParser; private final SelectStatement selectStatement; @Setter private int parametersIndex; @Setter private boolean isInSubQuery; private boolean appendDerivedColumnsFlag; public AbstractSelectParser(final AbstractSQLParser sqlParser) { this.sqlParser = sqlParser; selectStatement = new SelectStatement(); } @Override public final SelectStatement parse() { sqlParser.getLexer().nextToken(); parseDistinct(); parseBeforeSelectList(); parseSelectList(); parseFrom(); parseWhere(); customizedBetweenWhereAndGroupBy(); parseGroupBy(); customizedBetweenGroupByAndOrderBy(); parseOrderBy(); customizedSelect(); processUnsupportedTokens(); // TODO move to rewrite appendDerivedColumns(); appendDerivedOrderBy(); return selectStatement; } private void parseDistinct() { sqlParser.skipAll(DefaultKeyword.ALL); Collection distinctKeywords = Lists.newLinkedList(getCustomizedDistinctKeywords()); distinctKeywords.add(DefaultKeyword.DISTINCT); if (getSqlParser().equalAny(distinctKeywords.toArray(new Keyword[distinctKeywords.size()]))) { throw new SQLParsingUnsupportedException(getSqlParser().getLexer().getCurrentToken().getType()); } } protected Collection getCustomizedDistinctKeywords() { return Collections.emptyList(); } protected void parseBeforeSelectList() { } private void parseSelectList() { do { parseSelectItem(); } while (sqlParser.skipIfEqual(Symbol.COMMA)); if (0 == selectStatement.getSelectListLastPosition()) { selectStatement.setSelectListLastPosition(sqlParser.getLexer().getCurrentToken().getEndPosition() - sqlParser.getLexer().getCurrentToken().getLiterals().length()); } } private void parseSelectItem() { if (isRowNumberSelectItem()) { selectStatement.getItems().add(parseRowNumberSelectItem()); return; } sqlParser.skipIfEqual(DefaultKeyword.CONNECT_BY_ROOT); String literals = sqlParser.getLexer().getCurrentToken().getLiterals(); if (isStarSelectItem(literals)) { selectStatement.getItems().add(parseStarSelectItem()); return; } if (isAggregationSelectItem()) { selectStatement.getItems().add(parseAggregationSelectItem(literals)); return; } StringBuilder expression = new StringBuilder(); Token lastToken = null; 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)); } } if (hasAlias(expression, lastToken)) { selectStatement.getItems().add(parseSelectItemWithAlias(expression, lastToken)); return; } selectStatement.getItems().add(new CommonSelectItem(SQLUtil.getExactlyValue(expression.toString()), sqlParser.parseAlias())); } protected boolean isRowNumberSelectItem() { return false; } protected SelectItem parseRowNumberSelectItem() { throw new UnsupportedOperationException("Cannot support special select item."); } private boolean isStarSelectItem(final String literals) { return sqlParser.equalAny(Symbol.STAR) || Symbol.STAR.getLiterals().equals(SQLUtil.getExactlyValue(literals)); } private SelectItem parseStarSelectItem() { sqlParser.getLexer().nextToken(); selectStatement.setContainStar(true); return new CommonSelectItem(Symbol.STAR.getLiterals(), sqlParser.parseAlias()); } private boolean isAggregationSelectItem() { return sqlParser.skipIfEqual(DefaultKeyword.MAX, DefaultKeyword.MIN, DefaultKeyword.SUM, DefaultKeyword.AVG, DefaultKeyword.COUNT); } private SelectItem parseAggregationSelectItem(final String literals) { return new AggregationSelectItem(AggregationType.valueOf(literals.toUpperCase()), sqlParser.skipParentheses(), sqlParser.parseAlias()); } private boolean hasAlias(final StringBuilder expression, final Token lastToken) { return null != lastToken && Literals.IDENTIFIER == lastToken.getType() && !isSQLPropertyExpression(expression, lastToken) && !expression.toString().equals(lastToken.getLiterals()); } private boolean isSQLPropertyExpression(final StringBuilder expression, final Token lastToken) { return expression.toString().endsWith(Symbol.DOT.getLiterals() + lastToken.getLiterals()); } private CommonSelectItem parseSelectItemWithAlias(final StringBuilder expression, final Token lastToken) { return new CommonSelectItem(SQLUtil.getExactlyValue(expression.substring(0, expression.lastIndexOf(lastToken.getLiterals()))), Optional.of(lastToken.getLiterals())); } protected final void parseWhere() { if (selectStatement.getTables().isEmpty()) { return; } sqlParser.parseWhere(selectStatement); parametersIndex = sqlParser.getParametersIndex(); } protected void customizedBetweenWhereAndGroupBy() { } protected void customizedBetweenGroupByAndOrderBy() { } protected final void parseOrderBy() { if (!sqlParser.skipIfEqual(DefaultKeyword.ORDER)) { return; } List result = new LinkedList<>(); sqlParser.skipIfEqual(DefaultKeyword.SIBLINGS); sqlParser.accept(DefaultKeyword.BY); do { OrderItem orderItem = parseSelectOrderByItem(); if (!isInSubQuery) { result.add(orderItem); } } while (sqlParser.skipIfEqual(Symbol.COMMA)); selectStatement.getOrderByItems().addAll(result); } private OrderItem parseSelectOrderByItem() { SQLExpression sqlExpression = sqlParser.parseExpression(selectStatement); OrderType orderByType = OrderType.ASC; if (sqlParser.skipIfEqual(DefaultKeyword.ASC)) { orderByType = OrderType.ASC; } else if (sqlParser.skipIfEqual(DefaultKeyword.DESC)) { orderByType = OrderType.DESC; } OrderItem result; if (sqlExpression instanceof SQLNumberExpression) { result = new OrderItem(((SQLNumberExpression) sqlExpression).getNumber().intValue(), orderByType); } else if (sqlExpression instanceof SQLIdentifierExpression) { result = new OrderItem( SQLUtil.getExactlyValue(((SQLIdentifierExpression) sqlExpression).getName()), orderByType, getAlias(SQLUtil.getExactlyValue(((SQLIdentifierExpression) sqlExpression).getName()))); } else if (sqlExpression instanceof SQLPropertyExpression) { SQLPropertyExpression sqlPropertyExpression = (SQLPropertyExpression) sqlExpression; result = new OrderItem(SQLUtil.getExactlyValue(sqlPropertyExpression.getOwner().getName()), SQLUtil.getExactlyValue(sqlPropertyExpression.getName()), orderByType, getAlias(SQLUtil.getExactlyValue(sqlPropertyExpression.getOwner().getName()) + "." + SQLUtil.getExactlyValue(sqlPropertyExpression.getName()))); } else if (sqlExpression instanceof SQLIgnoreExpression) { SQLIgnoreExpression sqlIgnoreExpression = (SQLIgnoreExpression) sqlExpression; result = new OrderItem(sqlIgnoreExpression.getExpression(), orderByType, getAlias(sqlIgnoreExpression.getExpression())); } else { throw new SQLParsingException(sqlParser.getLexer()); } skipAfterOrderByItem(); return result; } protected void skipAfterOrderByItem() { } protected void parseGroupBy() { if (sqlParser.skipIfEqual(DefaultKeyword.GROUP)) { sqlParser.accept(DefaultKeyword.BY); while (true) { addGroupByItem(sqlParser.parseExpression(selectStatement)); if (!sqlParser.equalAny(Symbol.COMMA)) { break; } sqlParser.getLexer().nextToken(); } while (sqlParser.equalAny(DefaultKeyword.WITH) || sqlParser.getLexer().getCurrentToken().getLiterals().equalsIgnoreCase("ROLLUP")) { sqlParser.getLexer().nextToken(); } if (sqlParser.skipIfEqual(DefaultKeyword.HAVING)) { throw new UnsupportedOperationException("Cannot support Having"); } selectStatement.setGroupByLastPosition(sqlParser.getLexer().getCurrentToken().getEndPosition() - getSqlParser().getLexer().getCurrentToken().getLiterals().length()); } else if (sqlParser.skipIfEqual(DefaultKeyword.HAVING)) { throw new UnsupportedOperationException("Cannot support Having"); } } protected final void addGroupByItem(final SQLExpression sqlExpression) { OrderType orderByType = OrderType.ASC; if (sqlParser.equalAny(DefaultKeyword.ASC)) { sqlParser.getLexer().nextToken(); } else if (sqlParser.skipIfEqual(DefaultKeyword.DESC)) { orderByType = OrderType.DESC; } OrderItem orderItem; if (sqlExpression instanceof SQLPropertyExpression) { SQLPropertyExpression sqlPropertyExpression = (SQLPropertyExpression) sqlExpression; orderItem = new OrderItem(SQLUtil.getExactlyValue(sqlPropertyExpression.getOwner().getName()), SQLUtil.getExactlyValue(sqlPropertyExpression.getName()), orderByType, getAlias(SQLUtil.getExactlyValue(sqlPropertyExpression.getOwner() + "." + SQLUtil.getExactlyValue(sqlPropertyExpression.getName())))); } else if (sqlExpression instanceof SQLIdentifierExpression) { SQLIdentifierExpression sqlIdentifierExpression = (SQLIdentifierExpression) sqlExpression; orderItem = new OrderItem(SQLUtil.getExactlyValue(sqlIdentifierExpression.getName()), orderByType, getAlias(SQLUtil.getExactlyValue(sqlIdentifierExpression.getName()))); } else { return; } if (!isInSubQuery) { selectStatement.getGroupByItems().add(orderItem); } } private Optional getAlias(final String name) { if (selectStatement.isContainStar()) { return Optional.absent(); } String rawName = SQLUtil.getExactlyValue(name); for (SelectItem each : selectStatement.getItems()) { if (rawName.equalsIgnoreCase(SQLUtil.getExactlyValue(each.getExpression()))) { return each.getAlias(); } if (rawName.equalsIgnoreCase(each.getAlias().orNull())) { return Optional.of(rawName); } } return Optional.absent(); } private void parseFrom() { if (getSqlParser().equalAny(DefaultKeyword.INTO)) { throw new SQLParsingUnsupportedException(DefaultKeyword.INTO); } if (sqlParser.skipIfEqual(DefaultKeyword.FROM)) { parseTable(); } } private void parseTable() { if (sqlParser.skipIfEqual(Symbol.LEFT_PAREN)) { if (!selectStatement.getTables().isEmpty()) { throw new UnsupportedOperationException("Cannot support subquery for nested tables."); } isInSubQuery = true; selectStatement.setContainStar(false); sqlParser.skipUselessParentheses(); parse(); sqlParser.skipUselessParentheses(); if (getSqlParser().equalAny(DefaultKeyword.WHERE, Assist.END)) { return; } } isInSubQuery = false; customizedParseTableFactor(); parseJoinTable(); } protected void customizedParseTableFactor() { parseTableFactor(); } protected final void parseTableFactor() { final int beginPosition = sqlParser.getLexer().getCurrentToken().getEndPosition() - sqlParser.getLexer().getCurrentToken().getLiterals().length(); sqlParser.skipAll(DefaultKeyword.AS); String literals = sqlParser.getLexer().getCurrentToken().getLiterals(); sqlParser.getLexer().nextToken(); // TODO 包含Schema解析 if (sqlParser.skipIfEqual(Symbol.DOT)) { sqlParser.getLexer().nextToken(); sqlParser.parseAlias(); return; } // FIXME 根据shardingRule过滤table selectStatement.getSqlTokens().add(new TableToken(beginPosition, literals)); selectStatement.getTables().add(new Table(SQLUtil.getExactlyValue(literals), sqlParser.parseAlias())); } protected void parseJoinTable() { if (sqlParser.skipJoin()) { parseTable(); if (sqlParser.skipIfEqual(DefaultKeyword.ON)) { do { parseTableCondition(sqlParser.getLexer().getCurrentToken().getEndPosition()); sqlParser.accept(Symbol.EQ); parseTableCondition(sqlParser.getLexer().getCurrentToken().getEndPosition() - sqlParser.getLexer().getCurrentToken().getLiterals().length()); } while (sqlParser.skipIfEqual(DefaultKeyword.AND)); } else if (sqlParser.skipIfEqual(DefaultKeyword.USING)) { sqlParser.skipParentheses(); } parseJoinTable(); } } private void parseTableCondition(final int startPosition) { SQLExpression sqlExpression = sqlParser.parseExpression(); if (!(sqlExpression instanceof SQLPropertyExpression)) { return; } SQLPropertyExpression sqlPropertyExpression = (SQLPropertyExpression) sqlExpression; if (selectStatement.getTables().getTableNames().contains(SQLUtil.getExactlyValue(sqlPropertyExpression.getOwner().getName()))) { selectStatement.getSqlTokens().add(new TableToken(startPosition, sqlPropertyExpression.getOwner().getName())); } } protected abstract void customizedSelect(); private void processUnsupportedTokens() { if (sqlParser.equalAny(DefaultKeyword.UNION, DefaultKeyword.EXCEPT, DefaultKeyword.INTERSECT, DefaultKeyword.MINUS)) { throw new SQLParsingUnsupportedException(sqlParser.getLexer().getCurrentToken().getType()); } } private void appendDerivedColumns() { if (appendDerivedColumnsFlag) { return; } appendDerivedColumnsFlag = true; ItemsToken itemsToken = new ItemsToken(selectStatement.getSelectListLastPosition()); appendAvgDerivedColumns(itemsToken); appendDerivedOrderColumns(itemsToken, selectStatement.getOrderByItems(), ORDER_BY_DERIVED_ALIAS); appendDerivedOrderColumns(itemsToken, selectStatement.getGroupByItems(), GROUP_BY_DERIVED_ALIAS); if (!itemsToken.getItems().isEmpty()) { selectStatement.getSqlTokens().add(itemsToken); } } private void appendAvgDerivedColumns(final ItemsToken itemsToken) { int derivedColumnOffset = 0; for (SelectItem each : selectStatement.getItems()) { if (!(each instanceof AggregationSelectItem) || AggregationType.AVG != ((AggregationSelectItem) each).getType()) { continue; } AggregationSelectItem avgItem = (AggregationSelectItem) each; String countAlias = String.format(DERIVED_COUNT_ALIAS, derivedColumnOffset); AggregationSelectItem countItem = new AggregationSelectItem(AggregationType.COUNT, avgItem.getInnerExpression(), Optional.of(countAlias)); String sumAlias = String.format(DERIVED_SUM_ALIAS, derivedColumnOffset); AggregationSelectItem sumItem = new AggregationSelectItem(AggregationType.SUM, avgItem.getInnerExpression(), Optional.of(sumAlias)); avgItem.getDerivedAggregationSelectItems().add(countItem); avgItem.getDerivedAggregationSelectItems().add(sumItem); // TODO 将AVG列替换成常数,避免数据库再计算无用的AVG函数 itemsToken.getItems().add(countItem.getExpression() + " AS " + countAlias + " "); itemsToken.getItems().add(sumItem.getExpression() + " AS " + sumAlias + " "); derivedColumnOffset++; } } private void appendDerivedOrderColumns(final ItemsToken itemsToken, final List orderItems, final String aliasPattern) { int derivedColumnOffset = 0; for (OrderItem each : orderItems) { if (!isContainsItem(each)) { String alias = String.format(aliasPattern, derivedColumnOffset++); each.setAlias(Optional.of(alias)); itemsToken.getItems().add(each.getQualifiedName().get() + " AS " + alias + " "); } } } private boolean isContainsItem(final OrderItem orderItem) { if (selectStatement.isContainStar()) { return true; } for (SelectItem each : selectStatement.getItems()) { if (-1 != orderItem.getIndex()) { return true; } if (each.getAlias().isPresent() && orderItem.getAlias().isPresent() && each.getAlias().get().equalsIgnoreCase(orderItem.getAlias().get())) { return true; } if (!each.getAlias().isPresent() && orderItem.getQualifiedName().isPresent() && each.getExpression().equalsIgnoreCase(orderItem.getQualifiedName().get())) { return true; } } return false; } private void appendDerivedOrderBy() { if (!getSelectStatement().getGroupByItems().isEmpty() && getSelectStatement().getOrderByItems().isEmpty()) { getSelectStatement().getOrderByItems().addAll(getSelectStatement().getGroupByItems()); getSelectStatement().getSqlTokens().add(new OrderByToken(getSelectStatement().getGroupByLastPosition())); } } }