/* * Copyright 1999-2015 dangdang.com. * <p> * 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. * </p> */ package com.dangdang.ddframe.rdb.sharding.parser.visitor.basic.mysql; import com.alibaba.druid.sql.ast.SQLHint; import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLInListExpr; 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.dialect.mysql.visitor.MySqlOutputVisitor; import com.dangdang.ddframe.rdb.sharding.constants.DatabaseType; import com.dangdang.ddframe.rdb.sharding.parser.result.router.Condition.BinaryOperator; import com.dangdang.ddframe.rdb.sharding.parser.result.router.SQLBuilder; import com.dangdang.ddframe.rdb.sharding.parser.result.router.Table; import com.dangdang.ddframe.rdb.sharding.parser.visitor.ParseContext; import com.dangdang.ddframe.rdb.sharding.parser.visitor.SQLVisitor; import com.dangdang.ddframe.rdb.sharding.util.SQLUtil; import java.util.Arrays; import java.util.Collections; /** * MySQL解析基础访问器. * * @author zhangliang */ public abstract class AbstractMySQLVisitor extends MySqlOutputVisitor implements SQLVisitor { private ParseContext parseContext; private int parseContextIndex; protected AbstractMySQLVisitor() { super(new SQLBuilder()); setPrettyFormat(false); parseContext = new ParseContext(parseContextIndex); } @Override public final DatabaseType getDatabaseType() { return DatabaseType.MySQL; } @Override public final ParseContext getParseContext() { return parseContext; } final void stepInQuery() { if (0 == parseContextIndex) { parseContextIndex++; return; } ParseContext parseContext = new ParseContext(parseContextIndex++); parseContext.setShardingRule(this.parseContext.getShardingRule()); parseContext.setParentParseContext(this.parseContext); this.parseContext.getSubParseContext().add(parseContext); this.parseContext = parseContext; } final void stepOutQuery() { if (null == parseContext.getParentParseContext()) { return; } parseContext = parseContext.getParentParseContext(); } @Override public final SQLBuilder getSQLBuilder() { return (SQLBuilder) appender; } @Override public final void printToken(final String token) { getSQLBuilder().appendToken(SQLUtil.getExactlyValue(token)); } @Override public final void printToken(final String label, final String token) { getSQLBuilder().appendToken(label, SQLUtil.getExactlyValue(token)); } /** * 父类使用<tt>@@</tt>代替<tt>?</tt>,此处直接输出参数占位符<tt>?</tt> * * @param x 变量表达式 * @return false 终止遍历AST */ @Override public final boolean visit(final SQLVariantRefExpr x) { print(x.getName()); return false; } @Override public final boolean visit(final SQLExprTableSource x) { if ("dual".equalsIgnoreCase(SQLUtil.getExactlyValue(x.getExpr().toString()))) { return super.visit(x); } return visit(x, getParseContext().addTable(x)); } private boolean visit(final SQLExprTableSource x, final Table table) { printToken(table.getName()); if (table.getAlias().isPresent()) { print(' '); print(table.getAlias().get()); } for (SQLHint each : x.getHints()) { print(' '); each.accept(this); } return false; } /** * 将表名替换成占位符. * * <p> * 1. 如果二元表达式使用别名, 如: * {@code FROM order o WHERE o.column_name = 't' }, 则Column中的tableName为o. * </p> * * <p> * 2. 如果二元表达式使用表名, 如: * {@code FROM order WHERE order.column_name = 't' }, 则Column中的tableName为order. * </p> * * @param x SQL属性表达式 * @return true表示继续遍历AST, false表示终止遍历AST */ @Override // TODO SELECT [别名.xxx]的情况,目前都是替换成token,解析之后应该替换回去 public final boolean visit(final SQLPropertyExpr x) { if (null != x.getParent() && !(x.getParent() instanceof SQLBinaryOpExpr) && !(x.getParent() instanceof SQLSelectItem) && !(x.getParent() instanceof SQLBetweenExpr)) { return super.visit(x); } if (!(x.getOwner() instanceof SQLIdentifierExpr)) { return super.visit(x); } String tableOrAliasName = ((SQLIdentifierExpr) x.getOwner()).getLowerName(); if (getParseContext().isBinaryOperateWithAlias(x, tableOrAliasName)) { return super.visit(x); } printToken(tableOrAliasName); print("."); print(x.getName()); return false; } @Override public boolean visit(final SQLBinaryOpExpr x) { switch (x.getOperator()) { case BooleanOr: parseContext.setHasOrCondition(true); break; case Equality: parseContext.addCondition(x.getLeft(), BinaryOperator.EQUAL, Collections.singletonList(x.getRight()), getDatabaseType(), getParameters()); parseContext.addCondition(x.getRight(), BinaryOperator.EQUAL, Collections.singletonList(x.getLeft()), getDatabaseType(), getParameters()); break; default: break; } return super.visit(x); } @Override public boolean visit(final SQLInListExpr x) { if (!x.isNot()) { parseContext.addCondition(x.getExpr(), BinaryOperator.IN, x.getTargetList(), getDatabaseType(), getParameters()); } return super.visit(x); } @Override public boolean visit(final SQLBetweenExpr x) { parseContext.addCondition(x.getTestExpr(), BinaryOperator.BETWEEN, Arrays.asList(x.getBeginExpr(), x.getEndExpr()), getDatabaseType(), getParameters()); return super.visit(x); } }