/** * 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.parser.visitor.basic.mysql; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; import com.alibaba.druid.sql.ast.expr.SQLNumericLiteralExpr; import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.ast.statement.SQLSelectItem; import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlSelectGroupByExpr; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor; import com.dangdang.ddframe.rdb.sharding.parser.result.merger.AggregationColumn; import com.dangdang.ddframe.rdb.sharding.parser.result.merger.AggregationColumn.AggregationType; import com.dangdang.ddframe.rdb.sharding.parser.result.merger.GroupByColumn; import com.dangdang.ddframe.rdb.sharding.parser.result.merger.Limit; import com.dangdang.ddframe.rdb.sharding.parser.result.merger.OrderByColumn; import com.dangdang.ddframe.rdb.sharding.parser.result.merger.OrderByColumn.OrderByType; import com.google.common.base.Optional; import com.google.common.base.Strings; import java.util.List; /** * MySQL的SELECT语句访问器. * * @author gaohongtao, zhangliang */ public class MySQLSelectVisitor extends AbstractMySQLVisitor { @Override protected void printSelectList(final List selectList) { super.printSelectList(selectList); // TODO 提炼成print,或者是否不应该由token的方式替换? getSQLBuilder().appendToken(getParseContext().getAutoGenTokenKey(), false); } @Override public boolean visit(final MySqlSelectQueryBlock x) { stepInQuery(); if (x.getFrom() instanceof SQLExprTableSource) { SQLExprTableSource tableExpr = (SQLExprTableSource) x.getFrom(); getParseContext().setCurrentTable(tableExpr.getExpr().toString(), Optional.fromNullable(tableExpr.getAlias())); } return super.visit(x); } /** * 解析 {@code SELECT item1,item2 FROM }中的item. * * @param x SELECT item 表达式 * @return true表示继续遍历AST, false表示终止遍历AST */ // TODO SELECT * 导致index不准,不支持SELECT *,且生产环境不建议使用SELECT * public boolean visit(final SQLSelectItem x) { getParseContext().increaseItemIndex(); if (Strings.isNullOrEmpty(x.getAlias())) { SQLExpr expr = x.getExpr(); if (expr instanceof SQLIdentifierExpr) { getParseContext().registerSelectItem(((SQLIdentifierExpr) expr).getName()); } else if (expr instanceof SQLPropertyExpr) { getParseContext().registerSelectItem(((SQLPropertyExpr) expr).getName()); } else if (expr instanceof SQLAllColumnExpr) { getParseContext().registerSelectItem("*"); } } else { getParseContext().registerSelectItem(x.getAlias()); } return super.visit(x); } @Override public boolean visit(final SQLAggregateExpr x) { if (!(x.getParent() instanceof SQLSelectItem)) { return super.visit(x); } AggregationType aggregationType; try { aggregationType = AggregationType.valueOf(x.getMethodName().toUpperCase()); } catch (final IllegalArgumentException ex) { return super.visit(x); } StringBuilder expression = new StringBuilder(); x.accept(new MySqlOutputVisitor(expression)); // TODO index获取不准,考虑使用别名替换 AggregationColumn column = new AggregationColumn(expression.toString(), aggregationType, Optional.fromNullable(((SQLSelectItem) x.getParent()).getAlias()), null == x.getOption() ? Optional.absent() : Optional.of(x.getOption().toString()), getParseContext().getItemIndex()); getParseContext().getParsedResult().getMergeContext().getAggregationColumns().add(column); if (AggregationType.AVG.equals(aggregationType)) { getParseContext().addDerivedColumnsForAvgColumn(column); // TODO 将AVG列替换成常数,避免数据库再计算无用的AVG函数 } return super.visit(x); } public boolean visit(final SQLOrderBy x) { for (SQLSelectOrderByItem each : x.getItems()) { SQLExpr expr = each.getExpr(); OrderByType orderByType = null == each.getType() ? OrderByType.ASC : OrderByType.valueOf(each.getType()); if (expr instanceof SQLIntegerExpr) { getParseContext().addOrderByColumn(((SQLIntegerExpr) expr).getNumber().intValue(), orderByType); } else if (expr instanceof SQLIdentifierExpr) { getParseContext().addOrderByColumn(((SQLIdentifierExpr) expr).getName(), orderByType); } else if (expr instanceof SQLPropertyExpr) { getParseContext().addOrderByColumn(((SQLPropertyExpr) expr).getName(), orderByType); } } return super.visit(x); } /** * 将GROUP BY列放入parseResult. * 直接返回false,防止重复解析GROUP BY表达式. * * @param x GROUP BY 表达式 * @return false 停止遍历AST */ @Override public boolean visit(final MySqlSelectGroupByExpr x) { String alias = getParseContext().generateDerivedColumnAlias(); OrderByType orderByType = null == x.getType() ? OrderByType.ASC : OrderByType.valueOf(x.getType()); if (x.getExpr() instanceof SQLPropertyExpr) { SQLPropertyExpr expr = (SQLPropertyExpr) x.getExpr(); getParseContext().addGroupByColumns(expr.toString(), alias, orderByType); } else if (x.getExpr() instanceof SQLIdentifierExpr) { SQLIdentifierExpr expr = (SQLIdentifierExpr) x.getExpr(); getParseContext().addGroupByColumns(expr.getName(), alias, orderByType); } return super.visit(x); } /** * LIMIT 解析. * * @param x LIMIT表达式 * @return false 停止遍历AST */ @Override public boolean visit(final MySqlSelectQueryBlock.Limit x) { print("LIMIT "); int offset = 0; if (null != x.getOffset()) { if (x.getOffset() instanceof SQLNumericLiteralExpr) { offset = ((SQLNumericLiteralExpr) x.getOffset()).getNumber().intValue(); print("0, "); } else { offset = ((Number) getParameters().get(((SQLVariantRefExpr) x.getOffset()).getIndex())).intValue(); getParameters().set(((SQLVariantRefExpr) x.getOffset()).getIndex(), 0); print("?, "); } } int rowCount; if (x.getRowCount() instanceof SQLNumericLiteralExpr) { rowCount = ((SQLNumericLiteralExpr) x.getRowCount()).getNumber().intValue(); print(rowCount + offset); } else { rowCount = ((Number) getParameters().get(((SQLVariantRefExpr) x.getRowCount()).getIndex())).intValue(); getParameters().set(((SQLVariantRefExpr) x.getRowCount()).getIndex(), rowCount + offset); print("?"); } getParseContext().getParsedResult().getMergeContext().setLimit(new Limit(offset, rowCount)); return false; } @Override public void endVisit(final MySqlSelectQueryBlock x) { StringBuilder derivedSelectItems = new StringBuilder(); for (AggregationColumn aggregationColumn : getParseContext().getParsedResult().getMergeContext().getAggregationColumns()) { for (AggregationColumn derivedColumn : aggregationColumn.getDerivedColumns()) { derivedSelectItems.append(", ").append(derivedColumn.getExpression()).append(" AS ").append(derivedColumn.getAlias().get()); } } for (GroupByColumn each : getParseContext().getParsedResult().getMergeContext().getGroupByColumns()) { derivedSelectItems.append(", ").append(each.getName()).append(" AS ").append(each.getAlias()); } for (OrderByColumn each : getParseContext().getParsedResult().getMergeContext().getOrderByColumns()) { if (each.getAlias().isPresent()) { derivedSelectItems.append(", ").append(each.getName().get()).append(" AS ").append(each.getAlias().get()); } } if (0 != derivedSelectItems.length()) { getSQLBuilder().buildSQL(getParseContext().getAutoGenTokenKey(), derivedSelectItems.toString()); } super.endVisit(x); stepOutQuery(); } }