/* * Copyright 1999-2012 Alibaba Group. * * 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. */ /** * (created at 2011-7-21) */ package com.alibaba.cobar.route.visitor; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import com.alibaba.cobar.config.model.TableConfig; import com.alibaba.cobar.parser.ast.ASTNode; import com.alibaba.cobar.parser.ast.expression.BinaryOperatorExpression; import com.alibaba.cobar.parser.ast.expression.Expression; import com.alibaba.cobar.parser.ast.expression.PolyadicOperatorExpression; import com.alibaba.cobar.parser.ast.expression.ReplacableExpression; import com.alibaba.cobar.parser.ast.expression.UnaryOperatorExpression; import com.alibaba.cobar.parser.ast.expression.comparison.BetweenAndExpression; import com.alibaba.cobar.parser.ast.expression.comparison.ComparisionEqualsExpression; import com.alibaba.cobar.parser.ast.expression.comparison.ComparisionIsExpression; import com.alibaba.cobar.parser.ast.expression.comparison.ComparisionNullSafeEqualsExpression; import com.alibaba.cobar.parser.ast.expression.comparison.InExpression; import com.alibaba.cobar.parser.ast.expression.logical.LogicalAndExpression; import com.alibaba.cobar.parser.ast.expression.logical.LogicalOrExpression; import com.alibaba.cobar.parser.ast.expression.misc.InExpressionList; import com.alibaba.cobar.parser.ast.expression.misc.QueryExpression; import com.alibaba.cobar.parser.ast.expression.misc.UserExpression; import com.alibaba.cobar.parser.ast.expression.primary.CaseWhenOperatorExpression; import com.alibaba.cobar.parser.ast.expression.primary.DefaultValue; import com.alibaba.cobar.parser.ast.expression.primary.ExistsPrimary; import com.alibaba.cobar.parser.ast.expression.primary.Identifier; import com.alibaba.cobar.parser.ast.expression.primary.MatchExpression; import com.alibaba.cobar.parser.ast.expression.primary.ParamMarker; import com.alibaba.cobar.parser.ast.expression.primary.PlaceHolder; import com.alibaba.cobar.parser.ast.expression.primary.RowExpression; import com.alibaba.cobar.parser.ast.expression.primary.SysVarPrimary; import com.alibaba.cobar.parser.ast.expression.primary.UsrDefVarPrimary; import com.alibaba.cobar.parser.ast.expression.primary.Wildcard; import com.alibaba.cobar.parser.ast.expression.primary.function.FunctionExpression; import com.alibaba.cobar.parser.ast.expression.primary.function.cast.Cast; import com.alibaba.cobar.parser.ast.expression.primary.function.cast.Convert; import com.alibaba.cobar.parser.ast.expression.primary.function.datetime.Extract; import com.alibaba.cobar.parser.ast.expression.primary.function.datetime.GetFormat; import com.alibaba.cobar.parser.ast.expression.primary.function.datetime.Timestampadd; import com.alibaba.cobar.parser.ast.expression.primary.function.datetime.Timestampdiff; import com.alibaba.cobar.parser.ast.expression.primary.function.groupby.Avg; import com.alibaba.cobar.parser.ast.expression.primary.function.groupby.Count; import com.alibaba.cobar.parser.ast.expression.primary.function.groupby.GroupConcat; import com.alibaba.cobar.parser.ast.expression.primary.function.groupby.Max; import com.alibaba.cobar.parser.ast.expression.primary.function.groupby.Min; import com.alibaba.cobar.parser.ast.expression.primary.function.groupby.Sum; import com.alibaba.cobar.parser.ast.expression.primary.function.string.Char; import com.alibaba.cobar.parser.ast.expression.primary.function.string.Trim; import com.alibaba.cobar.parser.ast.expression.primary.literal.IntervalPrimary; import com.alibaba.cobar.parser.ast.expression.primary.literal.LiteralBitField; import com.alibaba.cobar.parser.ast.expression.primary.literal.LiteralBoolean; import com.alibaba.cobar.parser.ast.expression.primary.literal.LiteralHexadecimal; import com.alibaba.cobar.parser.ast.expression.primary.literal.LiteralNull; import com.alibaba.cobar.parser.ast.expression.primary.literal.LiteralNumber; import com.alibaba.cobar.parser.ast.expression.primary.literal.LiteralString; import com.alibaba.cobar.parser.ast.expression.string.LikeExpression; import com.alibaba.cobar.parser.ast.expression.type.CollateExpression; import com.alibaba.cobar.parser.ast.fragment.GroupBy; import com.alibaba.cobar.parser.ast.fragment.Limit; import com.alibaba.cobar.parser.ast.fragment.OrderBy; import com.alibaba.cobar.parser.ast.fragment.SortOrder; import com.alibaba.cobar.parser.ast.fragment.ddl.ColumnDefinition; import com.alibaba.cobar.parser.ast.fragment.ddl.TableOptions; import com.alibaba.cobar.parser.ast.fragment.ddl.datatype.DataType; import com.alibaba.cobar.parser.ast.fragment.ddl.index.IndexColumnName; import com.alibaba.cobar.parser.ast.fragment.ddl.index.IndexOption; import com.alibaba.cobar.parser.ast.fragment.tableref.Dual; import com.alibaba.cobar.parser.ast.fragment.tableref.IndexHint; import com.alibaba.cobar.parser.ast.fragment.tableref.InnerJoin; import com.alibaba.cobar.parser.ast.fragment.tableref.NaturalJoin; import com.alibaba.cobar.parser.ast.fragment.tableref.OuterJoin; import com.alibaba.cobar.parser.ast.fragment.tableref.StraightJoin; import com.alibaba.cobar.parser.ast.fragment.tableref.SubqueryFactor; import com.alibaba.cobar.parser.ast.fragment.tableref.TableRefFactor; import com.alibaba.cobar.parser.ast.fragment.tableref.TableReference; import com.alibaba.cobar.parser.ast.fragment.tableref.TableReferences; import com.alibaba.cobar.parser.ast.stmt.dal.DALSetCharacterSetStatement; import com.alibaba.cobar.parser.ast.stmt.dal.DALSetNamesStatement; import com.alibaba.cobar.parser.ast.stmt.dal.DALSetStatement; import com.alibaba.cobar.parser.ast.stmt.dal.ShowAuthors; import com.alibaba.cobar.parser.ast.stmt.dal.ShowBinLogEvent; import com.alibaba.cobar.parser.ast.stmt.dal.ShowBinaryLog; import com.alibaba.cobar.parser.ast.stmt.dal.ShowCharaterSet; import com.alibaba.cobar.parser.ast.stmt.dal.ShowCollation; import com.alibaba.cobar.parser.ast.stmt.dal.ShowColumns; import com.alibaba.cobar.parser.ast.stmt.dal.ShowContributors; import com.alibaba.cobar.parser.ast.stmt.dal.ShowCreate; import com.alibaba.cobar.parser.ast.stmt.dal.ShowDatabases; import com.alibaba.cobar.parser.ast.stmt.dal.ShowEngine; import com.alibaba.cobar.parser.ast.stmt.dal.ShowEngines; import com.alibaba.cobar.parser.ast.stmt.dal.ShowErrors; import com.alibaba.cobar.parser.ast.stmt.dal.ShowEvents; import com.alibaba.cobar.parser.ast.stmt.dal.ShowFunctionCode; import com.alibaba.cobar.parser.ast.stmt.dal.ShowFunctionStatus; import com.alibaba.cobar.parser.ast.stmt.dal.ShowGrants; import com.alibaba.cobar.parser.ast.stmt.dal.ShowIndex; import com.alibaba.cobar.parser.ast.stmt.dal.ShowMasterStatus; import com.alibaba.cobar.parser.ast.stmt.dal.ShowOpenTables; import com.alibaba.cobar.parser.ast.stmt.dal.ShowPlugins; import com.alibaba.cobar.parser.ast.stmt.dal.ShowPrivileges; import com.alibaba.cobar.parser.ast.stmt.dal.ShowProcedureCode; import com.alibaba.cobar.parser.ast.stmt.dal.ShowProcedureStatus; import com.alibaba.cobar.parser.ast.stmt.dal.ShowProcesslist; import com.alibaba.cobar.parser.ast.stmt.dal.ShowProfile; import com.alibaba.cobar.parser.ast.stmt.dal.ShowProfiles; import com.alibaba.cobar.parser.ast.stmt.dal.ShowSlaveHosts; import com.alibaba.cobar.parser.ast.stmt.dal.ShowSlaveStatus; import com.alibaba.cobar.parser.ast.stmt.dal.ShowStatus; import com.alibaba.cobar.parser.ast.stmt.dal.ShowTableStatus; import com.alibaba.cobar.parser.ast.stmt.dal.ShowTables; import com.alibaba.cobar.parser.ast.stmt.dal.ShowTriggers; import com.alibaba.cobar.parser.ast.stmt.dal.ShowVariables; import com.alibaba.cobar.parser.ast.stmt.dal.ShowWarnings; import com.alibaba.cobar.parser.ast.stmt.ddl.DDLAlterTableStatement; import com.alibaba.cobar.parser.ast.stmt.ddl.DDLAlterTableStatement.AlterSpecification; import com.alibaba.cobar.parser.ast.stmt.ddl.DDLCreateIndexStatement; import com.alibaba.cobar.parser.ast.stmt.ddl.DDLCreateTableStatement; import com.alibaba.cobar.parser.ast.stmt.ddl.DDLDropIndexStatement; import com.alibaba.cobar.parser.ast.stmt.ddl.DDLDropTableStatement; import com.alibaba.cobar.parser.ast.stmt.ddl.DDLRenameTableStatement; import com.alibaba.cobar.parser.ast.stmt.ddl.DDLTruncateStatement; import com.alibaba.cobar.parser.ast.stmt.ddl.DescTableStatement; import com.alibaba.cobar.parser.ast.stmt.dml.DMLCallStatement; import com.alibaba.cobar.parser.ast.stmt.dml.DMLDeleteStatement; import com.alibaba.cobar.parser.ast.stmt.dml.DMLInsertReplaceStatement; import com.alibaba.cobar.parser.ast.stmt.dml.DMLInsertStatement; import com.alibaba.cobar.parser.ast.stmt.dml.DMLReplaceStatement; import com.alibaba.cobar.parser.ast.stmt.dml.DMLSelectStatement; import com.alibaba.cobar.parser.ast.stmt.dml.DMLSelectUnionStatement; import com.alibaba.cobar.parser.ast.stmt.dml.DMLUpdateStatement; import com.alibaba.cobar.parser.ast.stmt.extension.ExtDDLCreatePolicy; import com.alibaba.cobar.parser.ast.stmt.extension.ExtDDLDropPolicy; import com.alibaba.cobar.parser.ast.stmt.mts.MTSReleaseStatement; import com.alibaba.cobar.parser.ast.stmt.mts.MTSRollbackStatement; import com.alibaba.cobar.parser.ast.stmt.mts.MTSSavepointStatement; import com.alibaba.cobar.parser.ast.stmt.mts.MTSSetTransactionStatement; import com.alibaba.cobar.parser.util.ExprEvalUtils; import com.alibaba.cobar.parser.util.Pair; import com.alibaba.cobar.parser.visitor.SQLASTVisitor; import com.alibaba.cobar.util.SmallSet; /** * @author <a href="mailto:shuo.qius@alibaba-inc.com">QIU Shuo</a> */ public final class PartitionKeyVisitor implements SQLASTVisitor { private static final Set<Class<? extends Expression>> VERDICT_PASS_THROUGH_WHERE = new HashSet<Class<? extends Expression>>( 6); private static final Set<Class<? extends Expression>> GROUP_FUNC_PASS_THROUGH_SELECT = new HashSet<Class<? extends Expression>>( 5); private static final Set<Class<? extends Expression>> PARTITION_OPERAND_SINGLE = new HashSet<Class<? extends Expression>>( 3); static { VERDICT_PASS_THROUGH_WHERE.add(LogicalAndExpression.class); VERDICT_PASS_THROUGH_WHERE.add(LogicalOrExpression.class); VERDICT_PASS_THROUGH_WHERE.add(BetweenAndExpression.class); VERDICT_PASS_THROUGH_WHERE.add(InExpression.class); VERDICT_PASS_THROUGH_WHERE.add(ComparisionNullSafeEqualsExpression.class); VERDICT_PASS_THROUGH_WHERE.add(ComparisionEqualsExpression.class); GROUP_FUNC_PASS_THROUGH_SELECT.add(Count.class); GROUP_FUNC_PASS_THROUGH_SELECT.add(Sum.class); GROUP_FUNC_PASS_THROUGH_SELECT.add(Min.class); GROUP_FUNC_PASS_THROUGH_SELECT.add(Max.class); GROUP_FUNC_PASS_THROUGH_SELECT.add(Wildcard.class); PARTITION_OPERAND_SINGLE.add(BetweenAndExpression.class); PARTITION_OPERAND_SINGLE.add(ComparisionNullSafeEqualsExpression.class); PARTITION_OPERAND_SINGLE.add(ComparisionEqualsExpression.class); } private static boolean isVerdictPassthroughWhere(Expression node) { if (node == null) return false; return VERDICT_PASS_THROUGH_WHERE.contains(node.getClass()); } private static boolean isGroupFuncPassthroughSelect(Expression node) { if (node == null) return false; return GROUP_FUNC_PASS_THROUGH_SELECT.contains(node.getClass()); } public static boolean isPartitionKeyOperandSingle(Expression expr, ASTNode parent) { return parent == null && expr instanceof ReplacableExpression && PARTITION_OPERAND_SINGLE.contains(expr.getClass()); } public static boolean isPartitionKeyOperandIn(Expression expr, ASTNode parent) { return expr != null && parent instanceof InExpression; } // ---output------------------------------------------------------------------ public static final int GROUP_CANCEL = -1; public static final int GROUP_NON = 0; public static final int GROUP_SUM = 1; public static final int GROUP_MIN = 2; public static final int GROUP_MAX = 3; private boolean dual = false; private int groupFuncType = GROUP_NON; private long limitSize = -1L; private long limitOffset = -1L; private boolean tableMetaRead; private boolean rewriteField = false; private boolean schemaTrimmed = false; private boolean customedSchema = false; /** {tableNameUp -> {columnNameUp -> columnValues}}, obj[] never null */ private Map<String, Map<String, List<Object>>> columnValue = new HashMap<String, Map<String, List<Object>>>(2, 1); /** {table -> {column -> {value -> [(expr,parentExpr)]}}} */ private Map<String, Map<String, Map<Object, Set<Pair<Expression, ASTNode>>>>> columnValueIndex; private Map<String, String> tableAlias = new HashMap<String, String>(4, 1); private static final String[] EMPTY_STRING_ARRAY = new String[0]; public boolean isDual() { return dual; } public boolean isCustomedSchema() { return customedSchema; } public boolean isTableMetaRead() { return tableMetaRead; } public boolean isNeedRewriteField() { return rewriteField; } /** * @return null for statement not table meta read */ public String[] getMetaReadTable() { if (isTableMetaRead()) { Set<String> tables = columnValue.keySet(); if (tables == null || tables.isEmpty()) { return EMPTY_STRING_ARRAY; } String[] array = new String[tables.size()]; Iterator<String> iter = tables.iterator(); for (int i = 0; i < array.length; ++i) { array[i] = iter.next(); } return array; } return null; } public Map<String, String> getTableAlias() { return tableAlias; } /** * @return -1 for no limit */ public long getLimitOffset() { return limitOffset; } /** * @return -1 for no limit */ public long getLimitSize() { return limitSize; } /** * @return {@link #GROUP_NON} or {@link #GROUP_SUM}or {@link #GROUP_MIN}or * {@link #GROUP_MAX} */ public int getGroupFuncType() { return groupFuncType; } public boolean isSchemaTrimmed() { return schemaTrimmed; } /** @return never null */ public Map<String, Map<Object, Set<Pair<Expression, ASTNode>>>> getColumnIndex(String tableNameUp) { if (columnValueIndex == null) return Collections.emptyMap(); Map<String, Map<Object, Set<Pair<Expression, ASTNode>>>> index = columnValueIndex.get(tableNameUp); if (index == null || index.isEmpty()) return Collections.emptyMap(); return index; } /** * @return <code>table -> null</code> is possible */ public Map<String, Map<String, List<Object>>> getColumnValue() { return columnValue; } private void addTable(String tableNameUp) { addTable(tableNameUp, 2); } /** * @param initColumnMapSize 0 for emptyMap */ private void addTable(String tableNameUp, int initColumnMapSize) { if (columnValue.containsKey(tableNameUp)) return; Map<String, List<Object>> colMap; if (initColumnMapSize > 0) { colMap = new HashMap<String, List<Object>>(initColumnMapSize, 1); } else { colMap = Collections.emptyMap(); } columnValue.put(tableNameUp, colMap); } private void addColumnValueIndex(String table, String column, Object value, Expression expr, ASTNode parent) { Map<String, Map<Object, Set<Pair<Expression, ASTNode>>>> colMap = ensureColumnValueIndexByTable(table); Map<Object, Set<Pair<Expression, ASTNode>>> valMap = ensureColumnValueIndexObjMap(colMap, column); addIntoColumnValueIndex(valMap, value, expr, parent); } private void addIntoColumnValueIndex(Map<Object, Set<Pair<Expression, ASTNode>>> valMap, Object value, Expression expr, ASTNode parent) { Set<Pair<Expression, ASTNode>> exprSet = valMap.get(value); if (exprSet == null) { // exprSet = new HashSet<Pair<Expression, ASTNode>>(2, 1); exprSet = new SmallSet<Pair<Expression, ASTNode>>(); valMap.put(value, exprSet); } Pair<Expression, ASTNode> pair = new Pair<Expression, ASTNode>(expr, parent); exprSet.add(pair); } private Map<String, Map<Object, Set<Pair<Expression, ASTNode>>>> ensureColumnValueIndexByTable(String table) { if (columnValueIndex == null) { columnValueIndex = new HashMap<String, Map<String, Map<Object, Set<Pair<Expression, ASTNode>>>>>(2, 1); } Map<String, Map<Object, Set<Pair<Expression, ASTNode>>>> colMap = columnValueIndex.get(table); if (colMap == null) { colMap = new HashMap<String, Map<Object, Set<Pair<Expression, ASTNode>>>>(2, 1); columnValueIndex.put(table, colMap); } return colMap; } private Map<Object, Set<Pair<Expression, ASTNode>>> ensureColumnValueIndexObjMap(Map<String, Map<Object, Set<Pair<Expression, ASTNode>>>> colMap, String column) { Map<Object, Set<Pair<Expression, ASTNode>>> valMap = colMap.get(column); if (valMap == null) { valMap = new HashMap<Object, Set<Pair<Expression, ASTNode>>>(2, 1); colMap.put(column, valMap); } return valMap; } private void addColumnValue(String tableNameUp, String columnNameUp, Object value, Expression expr, ASTNode parent) { Map<String, List<Object>> colVals = ensureColumnValueByTable(tableNameUp); ensureColumnValueList(colVals, columnNameUp).add(value); addColumnValueIndex(tableNameUp, columnNameUp, value, expr, parent); } private Map<String, List<Object>> ensureColumnValueByTable(String tableNameUp) { Map<String, List<Object>> colVals = columnValue.get(tableNameUp); if (colVals == null) { colVals = new HashMap<String, List<Object>>(2, 1); columnValue.put(tableNameUp, colVals); } return colVals; } private List<Object> ensureColumnValueList(Map<String, List<Object>> columnValue, String column) { List<Object> list = columnValue.get(column); if (list == null) { list = new LinkedList<Object>(); columnValue.put(column, list); } return list; } // ---temp // state------------------------------------------------------------------ private final Map<Object, Object> evaluationParameter = Collections.emptyMap(); private final Map<String, TableConfig> tablesRuleConfig; private boolean verdictColumn = true; private int idLevel = 2; private boolean verdictGroupFunc = true; private String trimSchema; public PartitionKeyVisitor(Map<String, TableConfig> tables) { if (tables == null || tables.isEmpty()) { tables = Collections.emptyMap(); } this.tablesRuleConfig = tables; } public PartitionKeyVisitor setTrimSchema(String trimSchema) { if (trimSchema != null) { this.trimSchema = trimSchema.toUpperCase(); } return this; } private boolean isRuledColumn(String tableNameUp, String columnNameUp) { if (tableNameUp == null) { return false; } TableConfig config = tablesRuleConfig.get(tableNameUp); if (config != null) { return config.existsColumn(columnNameUp); } return false; } private void visitChild(int idLevel, boolean verdictColumn, boolean verdictGroupFunc, ASTNode... nodes) { if (nodes == null || nodes.length <= 0) return; int oldLevel = this.idLevel; boolean oldVerdict = this.verdictColumn; boolean oldverdictGroupFunc = this.verdictGroupFunc; this.idLevel = idLevel; this.verdictColumn = verdictColumn; this.verdictGroupFunc = verdictGroupFunc; try { for (ASTNode node : nodes) { if (node != null) node.accept(this); } } finally { this.verdictColumn = oldVerdict; this.idLevel = oldLevel; this.verdictGroupFunc = oldverdictGroupFunc; } } private void visitChild(int idLevel, boolean verdictColumn, boolean verdictGroupFunc, List<? extends ASTNode> nodes) { if (nodes == null || nodes.isEmpty()) return; int oldLevel = this.idLevel; boolean oldVerdict = this.verdictColumn; boolean oldverdictGroupFunc = this.verdictGroupFunc; this.idLevel = idLevel; this.verdictColumn = verdictColumn; this.verdictGroupFunc = verdictGroupFunc; try { for (ASTNode node : nodes) { if (node != null) node.accept(this); } } finally { this.verdictColumn = oldVerdict; this.idLevel = oldLevel; this.verdictGroupFunc = oldverdictGroupFunc; } } // -------------------------------------------------------------------------------- private void limit(Limit limit) { if (limit != null) { Object ls = limit.getSize(); if (ls instanceof Expression) ls = ((Expression) ls).evaluation(evaluationParameter); if (ls instanceof Number) limitSize = ((Number) ls).longValue(); Object lo = limit.getOffset(); if (lo instanceof Expression) lo = ((Expression) lo).evaluation(evaluationParameter); if (lo instanceof Number) this.limitOffset = ((Number) lo).longValue(); } } @Override public void visit(DMLSelectStatement node) { boolean verdictGroup = true; List<Expression> exprList = node.getSelectExprListWithoutAlias(); if (verdictGroupFunc) { for (Expression expr : exprList) { if (!isGroupFuncPassthroughSelect(expr)) { groupFuncType = GROUP_CANCEL; verdictGroup = false; break; } } limit(node.getLimit()); } visitChild(2, false, verdictGroupFunc && verdictGroup, exprList); TableReference tr = node.getTables(); visitChild(1, verdictColumn, verdictGroupFunc && verdictGroup, tr); Expression where = node.getWhere(); visitChild(2, verdictColumn, false, where); GroupBy group = node.getGroup(); visitChild(2, false, false, group); Expression having = node.getHaving(); visitChild(2, verdictColumn, false, having); OrderBy order = node.getOrder(); visitChild(2, false, false, order); } @Override public void visit(DMLSelectUnionStatement node) { visitChild(2, false, false, node.getOrderBy()); visitChild(2, false, false, node.getSelectStmtList()); } @Override public void visit(DMLUpdateStatement node) { TableReference tr = node.getTableRefs(); visitChild(1, false, false, tr); List<Pair<Identifier, Expression>> assignmentList = node.getValues(); if (assignmentList != null && !assignmentList.isEmpty()) { List<ASTNode> list = new ArrayList<ASTNode>(assignmentList.size() * 2); for (Pair<Identifier, Expression> p : assignmentList) { if (p == null) continue; list.add(p.getKey()); list.add(p.getValue()); } visitChild(2, false, false, list); } Expression where = node.getWhere(); visitChild(2, verdictColumn, false, where); OrderBy order = node.getOrderBy(); visitChild(2, false, false, order); } private void tableAsTableFactor(Identifier table) { int trim = table.trimParent(1, trimSchema); schemaTrimmed = schemaTrimmed || trim == Identifier.PARENT_TRIMED; customedSchema = customedSchema || trim == Identifier.PARENT_IGNORED; String tableName = table.getIdTextUpUnescape(); tableAlias.put(null, tableName); tableAlias.put(tableName, tableName); addTable(tableName); } @Override public void visit(DMLDeleteStatement node) { TableReference tr = node.getTableRefs(); List<Identifier> tbs = node.getTableNames(); if (tr == null) { Identifier table = tbs.get(0); tableAsTableFactor(table); } else { visitChild(1, verdictColumn, false, tr); for (Identifier tb : tbs) { if (tb instanceof Wildcard) { int trim = tb.trimParent(2, trimSchema); schemaTrimmed = schemaTrimmed || trim == Identifier.PARENT_TRIMED; customedSchema = customedSchema || trim == Identifier.PARENT_IGNORED; } else { int trim = tb.trimParent(1, trimSchema); schemaTrimmed = schemaTrimmed || trim == Identifier.PARENT_TRIMED; customedSchema = customedSchema || trim == Identifier.PARENT_IGNORED; } } } Expression where = node.getWhereCondition(); visitChild(2, verdictColumn, false, where); if (tr == null) { OrderBy order = node.getOrderBy(); visitChild(2, false, false, order); } } private void insertReplace(DMLInsertReplaceStatement node) { Identifier table = node.getTable(); List<Identifier> collist = node.getColumnNameList(); QueryExpression query = node.getSelect(); List<RowExpression> rows = node.getRowList(); tableAsTableFactor(table); String tableName = table.getIdTextUpUnescape(); visitChild(2, false, false, collist); if (query != null) { query.accept(this); return; } for (RowExpression row : rows) { visitChild(2, false, false, row); } Map<String, List<Object>> colVals = ensureColumnValueByTable(tableName); Map<String, Map<Object, Set<Pair<Expression, ASTNode>>>> colValsIndex = ensureColumnValueIndexByTable(tableName); if (collist != null) { for (int i = 0; i < collist.size(); ++i) { String colName = collist.get(i).getIdTextUpUnescape(); if (isRuledColumn(tableName, colName)) { List<Object> valueList = ensureColumnValueList(colVals, colName); Map<Object, Set<Pair<Expression, ASTNode>>> valMap = ensureColumnValueIndexObjMap( colValsIndex, colName); for (RowExpression row : rows) { Expression expr = row.getRowExprList().get(i); Object value = expr == null ? null : expr.evaluation(evaluationParameter); if (value != Expression.UNEVALUATABLE) { valueList.add(value); addIntoColumnValueIndex(valMap, value, row, node); } } } } } } @Override public void visit(DMLInsertStatement node) { insertReplace(node); List<Pair<Identifier, Expression>> dup = node.getDuplicateUpdate(); if (dup != null) { ASTNode[] duplist = new ASTNode[dup.size() * 2]; int i = 0; for (Pair<Identifier, Expression> p : dup) { Identifier key = null; Expression value = null; if (p != null) { key = p.getKey(); value = p.getValue(); } duplist[i++] = key; duplist[i++] = value; } visitChild(2, false, false, duplist); } } @Override public void visit(DMLReplaceStatement node) { insertReplace(node); } private void ddlTable(Identifier id, int idLevel) { visitChild(idLevel, false, false, id); addTable(id.getIdTextUpUnescape()); } @Override public void visit(DDLTruncateStatement node) { ddlTable(node.getTable(), 1); } @Override public void visit(DDLAlterTableStatement node) { ddlTable(node.getTable(), 1); } @Override public void visit(DDLCreateIndexStatement node) { ddlTable(node.getTable(), 1); ddlTable(node.getIndexName(), 1); } @Override public void visit(DDLCreateTableStatement node) { ddlTable(node.getTable(), 1); } @Override public void visit(DDLRenameTableStatement node) { List<Pair<Identifier, Identifier>> list = node.getList(); List<Identifier> idl = new ArrayList<Identifier>(list.size() * 2); for (Pair<Identifier, Identifier> p : list) { if (p != null) { if (p.getKey() != null) { addTable(p.getKey().getIdTextUpUnescape()); idl.add(p.getKey()); } idl.add(p.getValue()); } } visitChild(1, false, false, idl); } @Override public void visit(DDLDropIndexStatement node) { ddlTable(node.getTable(), 1); ddlTable(node.getIndexName(), 1); } @Override public void visit(DDLDropTableStatement node) { visitChild(1, false, false, node.getTableNames()); List<Identifier> tbs = node.getTableNames(); if (tbs != null) { for (Identifier tb : tbs) { addTable(tb.getIdTextUpUnescape()); } } } @Override public void visit(BetweenAndExpression node) { Expression fst = node.getFirst(); Expression snd = node.getSecond(); Expression trd = node.getThird(); visitChild(2, false, false, fst, snd, trd); if (verdictColumn && !node.isNot() && fst instanceof Identifier) { Identifier col = (Identifier) fst; String table = tableAlias.get(col.getLevelUnescapeUpName(2)); if (isRuledColumn(table, col.getIdTextUpUnescape())) { Object e1 = snd.evaluation(evaluationParameter); Object e2 = trd.evaluation(evaluationParameter); if (e1 != Expression.UNEVALUATABLE && e2 != Expression.UNEVALUATABLE && e1 != null && e2 != null) { if (compareEvaluatedValue(e1, e2)) { addColumnValue(table, col.getIdTextUpUnescape(), e1, node, null); } } } } } /** * @param obj1 not null * @param obj2 not null */ private static boolean compareEvaluatedValue(Object obj1, Object obj2) { if (obj1.equals(obj2)) return true; try { Pair<Number, Number> pair = ExprEvalUtils.convertNum2SameLevel(obj1, obj2); return pair.getKey().equals(pair.getValue()); } catch (Exception e) { return false; } } @Override public void visit(ComparisionIsExpression node) { Expression operand = node.getOperand(); visitChild(2, false, false, operand); if (verdictColumn && (operand instanceof Identifier)) { Identifier col = (Identifier) operand; String table = tableAlias.get(col.getLevelUnescapeUpName(2)); if (isRuledColumn(table, col.getIdTextUpUnescape())) { switch (node.getMode()) { case ComparisionIsExpression.IS_FALSE: addColumnValue(table, col.getIdTextUpUnescape(), LiteralBoolean.FALSE, node, null); break; case ComparisionIsExpression.IS_TRUE: addColumnValue(table, col.getIdTextUpUnescape(), LiteralBoolean.TRUE, node, null); break; case ComparisionIsExpression.IS_NULL: addColumnValue(table, col.getIdTextUpUnescape(), null, node, null); break; } } } } @Override public void visit(InExpressionList node) { visitChild(2, false, false, node.getList()); } @Override public void visit(BinaryOperatorExpression node) { Expression left = node.getLeftOprand(); Expression right = node.getRightOprand(); visitChild(2, false, false, left, right); } @Override public void visit(PolyadicOperatorExpression node) { // QS_TODO } @Override public void visit(ComparisionEqualsExpression node) { Expression left = node.getLeftOprand(); Expression right = node.getRightOprand(); visitChild(2, false, false, left, right); if (verdictColumn) { if (left instanceof Identifier) { comparisionEquals((Identifier) left, right.evaluation(evaluationParameter), false, node); } else if (right instanceof Identifier) { comparisionEquals((Identifier) right, left.evaluation(evaluationParameter), false, node); } } } @Override public void visit(ComparisionNullSafeEqualsExpression node) { Expression left = node.getLeftOprand(); Expression right = node.getRightOprand(); visitChild(2, false, false, left, right); if (verdictColumn) { if (left instanceof Identifier) { comparisionEquals((Identifier) left, right.evaluation(evaluationParameter), true, node); } else if (right instanceof Identifier) { comparisionEquals((Identifier) right, left.evaluation(evaluationParameter), true, node); } } } private void comparisionEquals(Identifier col, Object value, boolean nullsafe, Expression node) { if (value != Expression.UNEVALUATABLE && (nullsafe || value != null)) { String table = tableAlias.get(col.getLevelUnescapeUpName(2)); if (isRuledColumn(table, col.getIdTextUpUnescape())) { addColumnValue(table, col.getIdTextUpUnescape(), value, node, null); } } } @Override public void visit(InExpression node) { Expression left = node.getLeftOprand(); Expression right = node.getRightOprand(); visitChild(2, false, false, left, right); if (verdictColumn && !node.isNot() && left instanceof Identifier && right instanceof InExpressionList) { Identifier col = (Identifier) left; String colName = col.getIdTextUpUnescape(); String table = tableAlias.get(col.getLevelUnescapeUpName(2)); if (isRuledColumn(table, colName)) { List<Object> valList = ensureColumnValueList(ensureColumnValueByTable(table), colName); Map<Object, Set<Pair<Expression, ASTNode>>> valMap = ensureColumnValueIndexObjMap( ensureColumnValueIndexByTable(table), colName); InExpressionList inlist = (InExpressionList) right; for (Expression expr : inlist.getList()) { Object value = expr.evaluation(evaluationParameter); if (value != Expression.UNEVALUATABLE) { valList.add(value); addIntoColumnValueIndex(valMap, value, expr, node); } } } } } @Override public void visit(LogicalAndExpression node) { for (int i = 0, len = node.getArity(); i < len; ++i) { Expression oprand = node.getOperand(i); visitChild(2, verdictColumn && isVerdictPassthroughWhere(oprand), false, oprand); } } @Override public void visit(LogicalOrExpression node) { for (int i = 0, len = node.getArity(); i < len; ++i) { Expression oprand = node.getOperand(i); visitChild(2, verdictColumn && isVerdictPassthroughWhere(oprand), false, oprand); } } @Override public void visit(Count node) { visitChild(2, false, false, node.getArguments()); if (verdictGroupFunc) { if (groupFuncType != GROUP_NON && groupFuncType != GROUP_SUM || node.isDistinct()) { groupFuncType = GROUP_CANCEL; } else { groupFuncType = GROUP_SUM; } } } @Override public void visit(Sum node) { visitChild(2, false, false, node.getArguments()); if (verdictGroupFunc) { if (groupFuncType != GROUP_NON && groupFuncType != GROUP_SUM || node.isDistinct()) { groupFuncType = GROUP_CANCEL; } else { groupFuncType = GROUP_SUM; } } } @Override public void visit(Max node) { visitChild(2, false, false, node.getArguments()); if (verdictGroupFunc) { if (groupFuncType != GROUP_NON && groupFuncType != GROUP_MAX) { groupFuncType = GROUP_CANCEL; } else { groupFuncType = GROUP_MAX; } } } @Override public void visit(Min node) { visitChild(2, false, false, node.getArguments()); if (verdictGroupFunc) { if (groupFuncType != GROUP_NON && groupFuncType != GROUP_MIN) { groupFuncType = GROUP_CANCEL; } else { groupFuncType = GROUP_MIN; } } } @Override public void visit(Identifier node) { int trim = node.trimParent(idLevel, trimSchema); schemaTrimmed = schemaTrimmed || trim == Identifier.PARENT_TRIMED; customedSchema = customedSchema || trim == Identifier.PARENT_IGNORED; } @Override public void visit(InnerJoin node) { TableReference tr1 = node.getLeftTableRef(); TableReference tr2 = node.getRightTableRef(); Expression on = node.getOnCond(); visitChild(1, verdictColumn, verdictGroupFunc, tr1, tr2); visitChild(2, verdictColumn && isVerdictPassthroughWhere(on), false, on); } @Override public void visit(NaturalJoin node) { TableReference tr1 = node.getLeftTableRef(); TableReference tr2 = node.getRightTableRef(); visitChild(1, verdictColumn, verdictGroupFunc, tr1, tr2); } @Override public void visit(OuterJoin node) { TableReference tr1 = node.getLeftTableRef(); TableReference tr2 = node.getRightTableRef(); Expression on = node.getOnCond(); visitChild(1, verdictColumn, verdictGroupFunc, tr1, tr2); visitChild(2, verdictColumn && isVerdictPassthroughWhere(on), false, on); } @Override public void visit(StraightJoin node) { TableReference tr1 = node.getLeftTableRef(); TableReference tr2 = node.getRightTableRef(); Expression on = node.getOnCond(); visitChild(1, verdictColumn, verdictGroupFunc, tr1, tr2); visitChild(2, verdictColumn && isVerdictPassthroughWhere(on), false, on); } @Override public void visit(TableReferences node) { List<TableReference> list = node.getTableReferenceList(); visitChild(1, verdictColumn, verdictGroupFunc, list); } @Override public void visit(SubqueryFactor node) { QueryExpression query = node.getSubquery(); visitChild(2, verdictColumn, verdictGroupFunc, query); } @Override public void visit(TableRefFactor node) { Identifier table = node.getTable(); visitChild(1, false, false, table); String tableName = table.getIdTextUpUnescape(); addTable(tableName); String alias = node.getAliasUnescapeUppercase(); if (alias == null) { tableAlias.put(null, tableName); tableAlias.put(tableName, tableName); } else { if (!tableAlias.containsKey(null)) { tableAlias.put(null, tableName); } tableAlias.put(alias, tableName); } } @Override public void visit(Dual dual) { this.dual = true; } // ------------------------------------------------------------------------------ @Override public void visit(LikeExpression node) { visitChild(2, false, false, node.getFirst(), node.getSecond(), node.getThird()); } @Override public void visit(CollateExpression node) { visitChild(2, false, false, node.getString()); } @Override public void visit(UserExpression node) { } @Override public void visit(UnaryOperatorExpression node) { visitChild(2, false, false, node.getOperand()); } @Override public void visit(FunctionExpression node) { visitChild(2, false, false, node.getArguments()); } @Override public void visit(Char node) { visitChild(2, false, false, node.getArguments()); } @Override public void visit(Convert node) { visitChild(2, false, false, node.getArguments()); } @Override public void visit(Trim node) { visitChild(2, false, false, node.getArguments()); } @Override public void visit(Cast node) { visitChild(2, false, false, node.getArguments()); visitChild(2, false, false, node.getTypeInfo1(), node.getTypeInfo2()); } @Override public void visit(Avg node) { visitChild(2, false, false, node.getArguments()); } @Override public void visit(GroupConcat node) { visitChild(2, false, false, node.getArguments()); } @Override public void visit(IntervalPrimary node) { visitChild(2, false, false, node.getQuantity()); } @Override public void visit(Extract node) { visitChild(2, false, false, node.getArguments()); } @Override public void visit(Timestampdiff node) { visitChild(2, false, false, node.getArguments()); } @Override public void visit(Timestampadd node) { visitChild(2, false, false, node.getArguments()); } @Override public void visit(GetFormat node) { } @Override public void visit(LiteralBitField node) { } @Override public void visit(LiteralBoolean node) { } @Override public void visit(LiteralHexadecimal node) { } @Override public void visit(LiteralNull node) { } @Override public void visit(LiteralNumber node) { } @Override public void visit(LiteralString node) { } @Override public void visit(CaseWhenOperatorExpression node) { visitChild(2, false, false, node.getComparee(), node.getElseResult()); List<Pair<Expression, Expression>> whenPairList = node.getWhenList(); if (whenPairList == null || whenPairList.isEmpty()) return; List<Expression> list = new ArrayList<Expression>(whenPairList.size() * 2); for (Pair<Expression, Expression> pair : whenPairList) { if (pair == null) continue; list.add(pair.getKey()); list.add(pair.getValue()); } visitChild(2, false, false, list); } @Override public void visit(DefaultValue node) { } @Override public void visit(ExistsPrimary node) { visitChild(2, false, false, node.getSubquery()); } @Override public void visit(PlaceHolder node) { } @Override public void visit(MatchExpression node) { visitChild(2, false, false, node.getColumns()); visitChild(2, false, false, node.getPattern()); } @Override public void visit(ParamMarker node) { } @Override public void visit(RowExpression node) { visitChild(2, false, false, node.getRowExprList()); } @Override public void visit(SysVarPrimary node) { } @Override public void visit(UsrDefVarPrimary node) { } @Override public void visit(IndexHint node) { } @Override public void visit(GroupBy node) { sortPairList(node.getOrderByList()); } @Override public void visit(OrderBy node) { sortPairList(node.getOrderByList()); } private void sortPairList(List<Pair<Expression, SortOrder>> list) { if (list == null || list.isEmpty()) return; Expression[] exprs = new Expression[list.size()]; int i = 0; for (Pair<Expression, SortOrder> p : list) { exprs[i] = p.getKey(); ++i; } visitChild(2, false, false, exprs); } @Override public void visit(Limit node) { } @Override public void visit(ColumnDefinition node) { } @Override public void visit(IndexOption node) { } @Override public void visit(IndexColumnName node) { } @Override public void visit(TableOptions node) { } @Override public void visit(AlterSpecification node) { } @Override public void visit(DataType node) { } @Override public void visit(ShowAuthors node) { } @Override public void visit(ShowBinaryLog node) { } @Override public void visit(ShowBinLogEvent node) { } @Override public void visit(ShowCharaterSet node) { } @Override public void visit(ShowCollation node) { } @Override public void visit(ShowColumns node) { tableMetaRead(node.getTable()); } @Override public void visit(ShowContributors node) { } @Override public void visit(ShowCreate node) { if (node.getType() == ShowCreate.Type.TABLE) { tableMetaRead(node.getId()); } } @Override public void visit(ShowDatabases node) { } @Override public void visit(ShowEngine node) { } @Override public void visit(ShowEngines node) { } @Override public void visit(ShowErrors node) { } @Override public void visit(ShowEvents node) { if (node.getSchema() != null) { schemaTrimmed = true; node.setSchema(null); } tableMetaRead(null); } @Override public void visit(ShowFunctionCode node) { } @Override public void visit(ShowFunctionStatus node) { } @Override public void visit(ShowGrants node) { } @Override public void visit(ShowIndex node) { tableMetaRead(node.getTable()); } @Override public void visit(ShowMasterStatus node) { } @Override public void visit(ShowOpenTables node) { if (node.getSchema() != null) { schemaTrimmed = true; node.setSchema(null); } tableMetaRead(null); } @Override public void visit(ShowPlugins node) { } @Override public void visit(ShowPrivileges node) { } @Override public void visit(ShowProcedureCode node) { } @Override public void visit(ShowProcedureStatus node) { } @Override public void visit(ShowProcesslist node) { } @Override public void visit(ShowProfile node) { } @Override public void visit(ShowProfiles node) { } @Override public void visit(ShowSlaveHosts node) { } @Override public void visit(ShowSlaveStatus node) { } @Override public void visit(ShowStatus node) { } @Override public void visit(ShowTables node) { if (node.getSchema() != null) { schemaTrimmed = true; node.setSchema(null); } rewriteField = true; tableMetaRead(null); } @Override public void visit(ShowTableStatus node) { if (node.getDatabase() != null) { schemaTrimmed = true; node.setDatabase(null); } tableMetaRead(null); } @Override public void visit(ShowTriggers node) { if (node.getSchema() != null) { schemaTrimmed = true; node.setSchema(null); } tableMetaRead(null); } @Override public void visit(ShowVariables node) { } @Override public void visit(ShowWarnings node) { } @Override public void visit(DescTableStatement node) { tableMetaRead(node.getTable()); } private void tableMetaRead(Identifier table) { if (table != null) { visitChild(1, false, false, table); addTable(table.getIdTextUpUnescape(), 0); } tableMetaRead = true; } @Override public void visit(DALSetStatement node) { } @Override public void visit(DALSetNamesStatement node) { } @Override public void visit(DALSetCharacterSetStatement node) { } @Override public void visit(DMLCallStatement node) { } @Override public void visit(MTSSetTransactionStatement node) { } @Override public void visit(MTSSavepointStatement node) { } @Override public void visit(MTSReleaseStatement node) { } @Override public void visit(MTSRollbackStatement node) { } @Override public void visit(ExtDDLCreatePolicy node) { } @Override public void visit(ExtDDLDropPolicy node) { } }