package com.taobao.tddl.repo.mysql.sqlconvertor; import java.sql.Types; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import com.taobao.tddl.common.jdbc.ParameterContext; import com.taobao.tddl.common.jdbc.ParameterMethod; import com.taobao.tddl.common.utils.TStringUtil; import com.taobao.tddl.optimizer.core.CanVisit; import com.taobao.tddl.optimizer.core.PlanVisitor; import com.taobao.tddl.optimizer.core.expression.IBooleanFilter; import com.taobao.tddl.optimizer.core.expression.IColumn; import com.taobao.tddl.optimizer.core.expression.IFilter; import com.taobao.tddl.optimizer.core.expression.IFilter.OPERATION; import com.taobao.tddl.optimizer.core.expression.IFunction; import com.taobao.tddl.optimizer.core.expression.IOrderBy; import com.taobao.tddl.optimizer.core.expression.ISelectable; import com.taobao.tddl.optimizer.core.expression.bean.NullValue; import com.taobao.tddl.optimizer.core.plan.IDataNodeExecutor; import com.taobao.tddl.optimizer.core.plan.IQueryTree; import com.taobao.tddl.optimizer.core.plan.dml.IDelete; import com.taobao.tddl.optimizer.core.plan.dml.IInsert; import com.taobao.tddl.optimizer.core.plan.dml.IReplace; import com.taobao.tddl.optimizer.core.plan.dml.IUpdate; import com.taobao.tddl.optimizer.core.plan.query.IJoin; import com.taobao.tddl.optimizer.core.plan.query.IQuery; import com.taobao.tddl.repo.mysql.sqlconvertor.functions.FunctionStringConstructor; import com.taobao.tddl.repo.mysql.sqlconvertor.functions.FunctionStringConstructorManager; public class MysqlPlanVisitorImpl implements PlanVisitor { protected boolean bindVal = true; protected Map<Integer, ParameterContext> paramMap; protected AtomicInteger bindValSequence; protected StringBuilder sqlBuilder = new StringBuilder(); protected FunctionStringConstructorManager manager = new FunctionStringConstructorManager(); protected IDataNodeExecutor query; protected boolean isGroupBy = false; private static Set<String> middleFuncName = new HashSet<String>(); static { middleFuncName.add("+"); middleFuncName.add("-"); middleFuncName.add("*"); middleFuncName.add("/"); middleFuncName.add("%"); middleFuncName.add("&"); middleFuncName.add("|"); middleFuncName.add("="); middleFuncName.add("!="); middleFuncName.add("LIKE"); middleFuncName.add("<>"); middleFuncName.add(":="); middleFuncName.add("IS"); middleFuncName.add("IN"); middleFuncName.add("CONSTANT"); middleFuncName.add("AND"); middleFuncName.add("OR"); middleFuncName.add("XOR"); middleFuncName.add(">>"); middleFuncName.add("<<"); middleFuncName.add("DIV"); middleFuncName.add("MOD"); middleFuncName.add("&&"); middleFuncName.add("||"); middleFuncName.add(">"); middleFuncName.add("<"); middleFuncName.add(">="); middleFuncName.add("<="); middleFuncName.add("<>"); middleFuncName.add("<=>"); middleFuncName.add("^"); } public MysqlPlanVisitorImpl(IDataNodeExecutor query, Map<Integer, ParameterContext> paramMap, AtomicInteger bindValSequence, boolean bindVal){ this(query, paramMap, bindValSequence, bindVal, false); } public MysqlPlanVisitorImpl(IDataNodeExecutor query, Map<Integer, ParameterContext> paramMap, AtomicInteger bindValSequence, boolean bindVal, boolean isGroupBy){ this.query = query; if (paramMap != null) { this.paramMap = paramMap; } else { this.paramMap = new HashMap<Integer, ParameterContext>(); } if (bindValSequence != null) { this.bindValSequence = bindValSequence; } else { this.bindValSequence = new AtomicInteger(1); } this.bindVal = bindVal; this.isGroupBy = isGroupBy; } protected void buildGroupBy(IQueryTree<IQueryTree> query) { boolean first = true; if (query.getGroupBys() != null && !query.getGroupBys().isEmpty()) { sqlBuilder.append(" group by "); first = true; for (IOrderBy order : query.getGroupBys()) { if (first) { first = false; } else { sqlBuilder.append(","); } MysqlPlanVisitorImpl visitor = this.getOrderbyVisitor(order, true); sqlBuilder.append(visitor.getString()); } } } protected void buildHaving(IQueryTree query) { if (query.getHavingFilter() != null) { sqlBuilder.append(" having "); MysqlPlanVisitorImpl visitor = this.getNewVisitor(query.getHavingFilter()); sqlBuilder.append(visitor.getString()); } } protected void buildLimit(IQueryTree query) { Long limitFrom = (Long) query.getLimitFrom(); Long limitTo = (Long) query.getLimitTo(); if ((limitFrom == null || limitFrom == -1) && (limitTo == null || limitTo == -1)) { return; } sqlBuilder.append(" limit "); MysqlPlanVisitorImpl visitor = this.getNewVisitor(limitFrom); sqlBuilder.append(visitor.getString()); if (limitTo != null && limitTo != -1) { visitor = this.getNewVisitor(limitTo); sqlBuilder.append(",").append(visitor.getString()); } } protected void buildOrderBy(IQueryTree<IQueryTree> query) { boolean first = true; if (query.getOrderBys() != null && !query.getOrderBys().isEmpty()) { sqlBuilder.append(" order by "); first = true; for (IOrderBy order : query.getOrderBys()) { if (first) { first = false; } else { sqlBuilder.append(","); } MysqlPlanVisitorImpl visitor = this.getOrderbyVisitor(order, false); sqlBuilder.append(visitor.getString()); } } } public void buildSelect(IQueryTree<IQueryTree> query) { sqlBuilder.append("select "); boolean hasDistinct = false; boolean first = true; StringBuilder sb = new StringBuilder(); for (ISelectable selected : query.getColumns()) { if (first) { first = false; } else { sb.append(","); } if (selected.isDistinct()) { hasDistinct = true; } MysqlPlanVisitorImpl visitor = this.getNewVisitor(selected); sb.append(visitor.getString()); if (selected.getAlias() != null) { sb.append(" as ").append(selected.getAlias()); } } if (hasDistinct) { sqlBuilder.append(" distinct "); } sqlBuilder.append(sb); } public MysqlPlanVisitorImpl getOrderbyVisitor(IOrderBy o, boolean isGroupBy) { MysqlPlanVisitorImpl visitor = new MysqlPlanVisitorImpl(query, paramMap, bindValSequence, bindVal, isGroupBy); if (o instanceof CanVisit) { ((CanVisit) o).accept(visitor); } else { visitor.visit(o); } return visitor; } public MysqlPlanVisitorImpl getNewVisitor(Object o) { MysqlPlanVisitorImpl visitor = new MysqlPlanVisitorImpl(query, paramMap, bindValSequence, bindVal, false); if (o instanceof CanVisit) { ((CanVisit) o).accept(visitor); } else { visitor.visit(o); } return visitor; } public MysqlPlanVisitorImpl getNewVisitor(IQueryTree query, Object o) { MysqlPlanVisitorImpl visitor = new MysqlPlanVisitorImpl(query, paramMap, bindValSequence, bindVal, false); if (o instanceof CanVisit) { ((CanVisit) o).accept(visitor); } else { visitor.visit(o); } return visitor; } public Map<Integer, ParameterContext> getParamMap() { return paramMap; } public String getString() { return sqlBuilder.toString(); } private boolean isMiddle(IFunction func) { if (middleFuncName.contains(func.getFunctionName())) { if (func.getArgs() != null && func.getArgs().size() == 1) { return false; } return true; } else { return false; } } public void setParamMap(Map<Integer, ParameterContext> paramMap) { this.paramMap = paramMap; } @Override public void visit(IColumn column) { // 别名加在select之外,如(select * from table) as t1,列名之前不能使用这个别名 // 别名加在select之内,如select * from table as t1,列名之前可以使用这个别名 if (query instanceof IQueryTree && !((IQueryTree) query).isSubQuery() && ((IQueryTree) query).getAlias() != null && column.getTableName() != null) { sqlBuilder.append(((IQueryTree) query).getAlias()); } else { if (query instanceof IQuery && column.getTableName() != null) { sqlBuilder.append(((IQuery) query).getTableName()); } else { if (query instanceof IQueryTree && column.getTableName() != null) { sqlBuilder.append(column.getTableName()); } else { sqlBuilder.append(column.getColumnName()); return; } } } sqlBuilder.append("."); sqlBuilder.append(column.getColumnName()); } @Override public void visit(IFilter filter) { visit((IFunction) filter); } @Override public void visit(IFunction func) { if (func.isNot()) { sqlBuilder.append("("); sqlBuilder.append(" NOT "); } String funcName = func.getFunctionName(); FunctionStringConstructor constructor = manager.getConstructor(func); if (constructor != null) { sqlBuilder.append(constructor.constructColumnNameForFunction(query, bindVal, bindValSequence, paramMap, func, this)); } else { boolean isMiddle = isMiddle(func); if (isMiddle) { sqlBuilder.append("("); if (func instanceof IFilter) { funcName = ((IFilter) func).getOperation().getOPERATIONString(); } } if ((func instanceof IFilter) && OPERATION.CONSTANT.equals(((IFilter) func).getOperation())) { MysqlPlanVisitorImpl visitor = this.getNewVisitor(func.getArgs().get(0)); sqlBuilder.append(visitor.getString());// 常量,不太可能走到这一步 } else if ((func instanceof IBooleanFilter) && (OPERATION.IS_NULL.equals(((IBooleanFilter) func).getOperation()) || OPERATION.IS_NOT_NULL.equals(((IBooleanFilter) func).getOperation()) || OPERATION.IS_TRUE.equals(((IBooleanFilter) func).getOperation()) || OPERATION.IS_NOT_TRUE.equals(((IBooleanFilter) func).getOperation()) || OPERATION.IS_FALSE.equals(((IBooleanFilter) func).getOperation()) || OPERATION.IS_NOT_FALSE.equals(((IBooleanFilter) func).getOperation()) )) { MysqlPlanVisitorImpl visitor = this.getNewVisitor(func.getArgs().get(0)); sqlBuilder.append(visitor.getString()); sqlBuilder.append(" ").append(funcName); } else { if (!isMiddle) { if (IFunction.BuiltInFunction.MINUS.equals(funcName)) { sqlBuilder.append("-"); } else if (!IFunction.BuiltInFunction.ROW.equals(funcName)) { // row代表向量匹配 sqlBuilder.append(funcName); } sqlBuilder.append("("); } boolean first = true; boolean isDistinct = false; StringBuilder argSb = new StringBuilder(); for (Object arg : func.getArgs()) { if (first) { first = false; } else if (isMiddle) { argSb.append(" ").append(funcName).append(" "); } else { argSb.append(","); } if (arg instanceof ISelectable && ((ISelectable) arg).isDistinct()) { isDistinct = true; } MysqlPlanVisitorImpl visitor = this.getNewVisitor(arg); argSb.append(visitor.getString()); } if (isDistinct) { sqlBuilder.append(" distinct "); } sqlBuilder.append(argSb); if (!isMiddle) { sqlBuilder.append(")"); } } if (isMiddle) { sqlBuilder.append(")"); } } if (func.isNot()) { sqlBuilder.append(")"); } } @Override public void visit(IJoin join) { if (join.isSubQuery() && !join.isTopQuery()) { sqlBuilder.append(" ( "); } if (join.isSubQuery() || join.isTopQuery()) { buildSelect(join); sqlBuilder.append(" from "); } IQueryTree left = join.getLeftNode(); IQueryTree right = join.getRightNode(); MysqlPlanVisitorImpl visitor = this.getNewVisitor(left, left); sqlBuilder.append(visitor.getString()); if (join.getLeftOuter() && join.getRightOuter()) { throw new RuntimeException("full outter join 不支持"); } else if (join.getLeftOuter() && !join.getRightOuter()) { sqlBuilder.append(" left"); } else if (join.getRightOuter() && !join.getLeftOuter()) { sqlBuilder.append(" right"); } sqlBuilder.append(" join "); visitor = this.getNewVisitor(right, right); sqlBuilder.append(visitor.getString()); sqlBuilder.append(" on "); StringBuilder joinOnFilterStr = new StringBuilder(); boolean first = true; for (int i = 0; i < join.getLeftJoinOnColumns().size(); i++) { if (first) { first = false; } else { joinOnFilterStr.append(" and "); } ISelectable leftColumn = join.getLeftJoinOnColumns().get(i); ISelectable rightColumn = join.getRightJoinOnColumns().get(i); joinOnFilterStr.append(this.getNewVisitor(leftColumn).getString()); joinOnFilterStr.append(" = "); joinOnFilterStr.append(this.getNewVisitor(rightColumn).getString()); } if (join.getOtherJoinOnFilter() != null) { if (first) { first = false; } else { joinOnFilterStr.append(" and "); } joinOnFilterStr.append(this.getNewVisitor(join.getOtherJoinOnFilter()).getString()); } sqlBuilder.append(joinOnFilterStr.toString()); if (join.isSubQuery() || join.isTopQuery()) { String whereFilterStr = ""; if (join.getWhereFilter() != null) { visitor = this.getNewVisitor(join.getWhereFilter()); whereFilterStr = visitor.getString(); } if (!TStringUtil.isEmpty(whereFilterStr)) { sqlBuilder.append(" where "); sqlBuilder.append(whereFilterStr); } buildGroupBy(join); buildHaving(join); buildOrderBy(join); buildLimit(join); } if (join.isSubQuery() && !join.isTopQuery()) { sqlBuilder.append(" ) "); if (join.getAlias() != null) sqlBuilder.append(" ").append(join.getAlias()).append(" "); } } @Override public void visit(IOrderBy orderBy) { MysqlPlanVisitorImpl visitor = this.getNewVisitor(orderBy.getColumn()); sqlBuilder.append(visitor.getString()); if (!isGroupBy) { if (orderBy.getDirection()) { sqlBuilder.append(" asc "); } else { sqlBuilder.append(" desc "); } } } @Override public void visit(IQuery query) { if (query.isSubQuery() && !query.isTopQuery()) { sqlBuilder.append(" ( "); } if (query.isSubQuery() || query.isTopQuery()) { buildSelect(query); if (query.getTableName() == null) { return; } sqlBuilder.append(" from "); } sqlBuilder.append(query.getTableName()); if (!query.isSubQuery() && query.getAlias() != null && !query.getAlias().equalsIgnoreCase(query.getTableName())) { sqlBuilder.append(" ").append(query.getAlias()); } if (query.isSubQuery() || query.isTopQuery()) { String keyFilterStr = ""; String resultFilterStr = ""; if (query.getKeyFilter() != null) { MysqlPlanVisitorImpl visitor = this.getNewVisitor(query.getKeyFilter()); keyFilterStr = visitor.getString(); } if (query.getValueFilter() != null) { MysqlPlanVisitorImpl visitor = this.getNewVisitor(query.getValueFilter()); resultFilterStr = visitor.getString(); } if (!TStringUtil.isEmpty(keyFilterStr) || !TStringUtil.isEmpty(resultFilterStr)) { sqlBuilder.append(" where "); sqlBuilder.append(keyFilterStr); if (!TStringUtil.isEmpty(keyFilterStr) && !TStringUtil.isEmpty(resultFilterStr)) { sqlBuilder.append(" and "); } sqlBuilder.append(resultFilterStr); } buildGroupBy(query); buildHaving(query); buildOrderBy(query); buildLimit(query); } if (query.isSubQuery() && !query.isTopQuery()) { sqlBuilder.append(" ) "); if (query.getAlias() != null) { sqlBuilder.append(" ").append(query.getAlias()).append(" "); } } } @Override public void visit(List cl) { List<Comparable> list = cl; sqlBuilder.append("("); boolean first = true; for (Comparable o : list) { if (first) { first = false; } else { sqlBuilder.append(","); } MysqlPlanVisitorImpl visitor = this.getNewVisitor(o); sqlBuilder.append(visitor.getString()); } sqlBuilder.append(")"); } @Override public void visit(NullValue nullValue) { sqlBuilder.append("null"); return; } @Override public void visit(Object s) { if (s instanceof List) { visit((List) s); } else { if (s instanceof Boolean) { sqlBuilder.append(((Boolean) s).toString()); return; } if (s instanceof NullValue) { sqlBuilder.append("null"); return; } int index = bindValSequence.getAndIncrement(); ParameterContext context = null; if (s != null && !(s instanceof NullValue)) { context = new ParameterContext(ParameterMethod.setObject1, new Object[] { index, s }); } else { context = new ParameterContext(ParameterMethod.setNull1, new Object[] { index, Types.NULL }); } this.paramMap.put(index, context); sqlBuilder.append("?"); } } @Override public void visit(IInsert put) { sqlBuilder.append("insert into "); sqlBuilder.append(put.getTableName()).append(" "); boolean first = true; if (put.getUpdateColumns() != null && !put.getUpdateValues().isEmpty()) { sqlBuilder.append("( "); for (int i = 0; i < put.getUpdateColumns().size(); i++) { if (first) { first = false; } else { sqlBuilder.append(", "); } sqlBuilder.append(this.getNewVisitor(put.getUpdateColumns().get(i)).getString()); } sqlBuilder.append(") "); } sqlBuilder.append("values ( "); first = true; for (int i = 0; i < put.getUpdateValues().size(); i++) { if (first) { first = false; } else { sqlBuilder.append(", "); } sqlBuilder.append(this.getNewVisitor(put.getUpdateValues().get(i)).getString()); } sqlBuilder.append(") "); } @Override public void visit(IReplace put) { sqlBuilder.append("replace into "); sqlBuilder.append(put.getTableName()).append(" "); boolean first = true; if (put.getUpdateColumns() != null && !put.getUpdateValues().isEmpty()) { sqlBuilder.append("( "); for (int i = 0; i < put.getUpdateColumns().size(); i++) { if (first) { first = false; } else { sqlBuilder.append(", "); } sqlBuilder.append(this.getNewVisitor(put.getUpdateColumns().get(i)).getString()); } sqlBuilder.append(") "); } sqlBuilder.append("values ( "); first = true; for (int i = 0; i < put.getUpdateValues().size(); i++) { if (first) { first = false; } else { sqlBuilder.append(", "); } sqlBuilder.append(this.getNewVisitor(put.getUpdateValues().get(i)).getString()); } sqlBuilder.append(") "); } @Override public void visit(IUpdate put) { sqlBuilder.append("update "); sqlBuilder.append(put.getTableName()).append(" "); sqlBuilder.append("set "); boolean first = true; if (put.getUpdateColumns() != null && !put.getUpdateValues().isEmpty()) { for (int i = 0; i < put.getUpdateColumns().size(); i++) { if (first) { first = false; } else { sqlBuilder.append(", "); } sqlBuilder.append(this.getNewVisitor(put.getUpdateColumns().get(i)).getString()); sqlBuilder.append(" = "); sqlBuilder.append(this.getNewVisitor(put.getUpdateValues().get(i)).getString()); } } String keyFilterStr = ""; String resultFilterStr = ""; IQueryTree query = put.getQueryTree(); if (query instanceof IQuery && ((IQuery) query).getKeyFilter() != null) { MysqlPlanVisitorImpl visitor = this.getNewVisitor(((IQuery) query).getKeyFilter()); keyFilterStr = visitor.getString(); } if (query.getValueFilter() != null) { MysqlPlanVisitorImpl visitor = this.getNewVisitor(query.getValueFilter()); resultFilterStr = visitor.getString(); } if (!TStringUtil.isEmpty(keyFilterStr) || !TStringUtil.isEmpty(resultFilterStr)) { sqlBuilder.append(" where "); sqlBuilder.append(keyFilterStr); if (!TStringUtil.isEmpty(keyFilterStr)) { if (!TStringUtil.isEmpty(resultFilterStr)) { sqlBuilder.append("and "); } } sqlBuilder.append(resultFilterStr); buildLimit(query); } } @Override public void visit(IDelete put) { sqlBuilder.append("delete from "); sqlBuilder.append(put.getTableName()).append(" "); String keyFilterStr = ""; String resultFilterStr = ""; IQueryTree query = put.getQueryTree(); if (query instanceof IQuery && ((IQuery) query).getKeyFilter() != null) { MysqlPlanVisitorImpl visitor = this.getNewVisitor(((IQuery) query).getKeyFilter()); keyFilterStr = visitor.getString(); } if (query.getValueFilter() != null) { MysqlPlanVisitorImpl visitor = this.getNewVisitor(query.getValueFilter()); resultFilterStr = visitor.getString(); } if (!TStringUtil.isEmpty(keyFilterStr) || !TStringUtil.isEmpty(resultFilterStr)) { sqlBuilder.append(" where "); sqlBuilder.append(keyFilterStr); if (!TStringUtil.isEmpty(keyFilterStr)) { if (!TStringUtil.isEmpty(resultFilterStr)) { sqlBuilder.append("and "); } } sqlBuilder.append(resultFilterStr); } buildLimit(query); } }