/* * Copyright 1999-2017 Alibaba Group Holding Ltd. * * 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.alibaba.druid.sql.visitor; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.alibaba.druid.sql.ast.SQLDeclareItem; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLObject; import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.SQLOrderingSpecification; import com.alibaba.druid.sql.ast.SQLParameter; import com.alibaba.druid.sql.ast.SQLPartition; import com.alibaba.druid.sql.ast.SQLPartitionByHash; import com.alibaba.druid.sql.ast.SQLPartitionByList; import com.alibaba.druid.sql.ast.SQLPartitionByRange; import com.alibaba.druid.sql.ast.SQLPartitionValue; import com.alibaba.druid.sql.ast.SQLSubPartition; import com.alibaba.druid.sql.ast.SQLSubPartitionByHash; import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; import com.alibaba.druid.sql.ast.expr.SQLArrayExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; import com.alibaba.druid.sql.ast.expr.SQLCastExpr; import com.alibaba.druid.sql.ast.expr.SQLCurrentOfCursorExpr; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLInListExpr; import com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr; import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; import com.alibaba.druid.sql.ast.expr.SQLSequenceExpr; import com.alibaba.druid.sql.ast.statement.*; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlExpr; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExplainStatement; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter; import com.alibaba.druid.sql.dialect.odps.ast.OdpsValuesTableSource; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleExpr; import com.alibaba.druid.sql.dialect.postgresql.visitor.PGASTVisitorAdapter; import com.alibaba.druid.stat.TableStat; import com.alibaba.druid.stat.TableStat.Column; import com.alibaba.druid.stat.TableStat.Condition; import com.alibaba.druid.stat.TableStat.Mode; import com.alibaba.druid.stat.TableStat.Relationship; import com.alibaba.druid.util.JdbcConstants; import com.alibaba.druid.util.StringUtils; public class SchemaStatVisitor extends SQLASTVisitorAdapter { protected final HashMap<TableStat.Name, TableStat> tableStats = new LinkedHashMap<TableStat.Name, TableStat>(); protected final Map<Column, Column> columns = new LinkedHashMap<Column, Column>(); protected final List<Condition> conditions = new ArrayList<Condition>(); protected final Set<Relationship> relationships = new LinkedHashSet<Relationship>(); protected final List<Column> orderByColumns = new ArrayList<Column>(); protected final Set<Column> groupByColumns = new LinkedHashSet<Column>(); protected final List<SQLAggregateExpr> aggregateFunctions = new ArrayList<SQLAggregateExpr>(); protected final List<SQLMethodInvokeExpr> functions = new ArrayList<SQLMethodInvokeExpr>(2); protected final Map<String, SQLObject> subQueryMap = new LinkedHashMap<String, SQLObject>(); protected final Map<String, SQLObject> variants = new LinkedHashMap<String, SQLObject>(); protected Map<String, String> aliasMap = new LinkedHashMap<String, String>(); protected String currentTable; public final static String ATTR_TABLE = "_table_"; public final static String ATTR_COLUMN = "_column_"; private List<Object> parameters; private Mode mode; public SchemaStatVisitor(){ this(new ArrayList<Object>()); } public SchemaStatVisitor(List<Object> parameters){ this.parameters = parameters; } public List<Object> getParameters() { return parameters; } public void setParameters(List<Object> parameters) { this.parameters = parameters; } public TableStat getTableStat(String ident) { return getTableStat(ident, null); } public Column addColumn(String tableName, String columnName) { tableName = handleName(tableName); columnName = handleName(columnName); Column column = this.getColumn(tableName, columnName); if (column == null && columnName != null) { column = new Column(tableName, columnName); columns.put(column, column); } return column; } public TableStat getTableStat(String tableName, String alias) { if (variants.containsKey(tableName)) { return null; } tableName = handleName(tableName); TableStat.Name tableNameObj = new TableStat.Name(tableName); TableStat stat = tableStats.get(tableNameObj); if (stat == null) { stat = new TableStat(); tableStats.put(new TableStat.Name(tableName), stat); putAliasMap(aliasMap, alias, tableName); } return stat; } private String handleName(String ident) { int len = ident.length(); if (ident.charAt(0) == '[' && ident.charAt(len - 1) == ']') { ident = ident.substring(1, len - 1); } else { boolean flag0 = false; boolean flag1 = false; boolean flag2 = false; boolean flag3 = false; for (int i = 0; i < len; ++i) { final char ch = ident.charAt(i); if (ch == '\"') { flag0 = true; } else if (ch == '`') { flag1 = true; } else if (ch == ' ') { flag2 = true; } else if (ch == '\'') { flag3 = true; } } if (flag0) { ident = ident.replaceAll("\"", ""); } if (flag1) { ident = ident.replaceAll("`", ""); } if (flag2) { ident = ident.replaceAll(" ", ""); } if (flag3) { ident = ident.replaceAll("'", ""); } } ident = aliasWrap(ident); return ident; } public Map<String, SQLObject> getVariants() { return variants; } public void setAliasMap() { this.setAliasMap(new HashMap<String, String>()); } public void clearAliasMap() { this.aliasMap = null; } public void setAliasMap(Map<String, String> aliasMap) { this.aliasMap = aliasMap; } public Map<String, String> getAliasMap() { return aliasMap; } public void setCurrentTable(String table) { this.currentTable = table; } public void setCurrentTable(SQLObject x) { x.putAttribute("_old_local_", this.currentTable); } public void restoreCurrentTable(SQLObject x) { String table = (String) x.getAttribute("_old_local_"); this.currentTable = table; } public void setCurrentTable(SQLObject x, String table) { x.putAttribute("_old_local_", this.currentTable); this.currentTable = table; } public String getCurrentTable() { return currentTable; } protected Mode getMode() { return mode; } protected void setModeOrigin(SQLObject x) { Mode originalMode = (Mode) x.getAttribute("_original_use_mode"); mode = originalMode; } protected Mode setMode(SQLObject x, Mode mode) { Mode oldMode = this.mode; x.putAttribute("_original_use_mode", oldMode); this.mode = mode; return oldMode; } private boolean visitOrderBy(SQLIdentifierExpr x) { if (containsSubQuery(currentTable)) { return false; } String identName = x.getName(); if (aliasMap != null && aliasMap.containsKey(identName) && aliasMap.get(identName) == null) { return false; } if (currentTable != null) { orderByAddColumn(currentTable, identName, x); } else { orderByAddColumn("UNKOWN", identName, x); } return false; } private boolean visitOrderBy(SQLPropertyExpr x) { if (x.getOwner() instanceof SQLIdentifierExpr) { String owner = ((SQLIdentifierExpr) x.getOwner()).getName(); if (containsSubQuery(owner)) { return false; } owner = aliasWrap(owner); if (owner != null) { orderByAddColumn(owner, x.getName(), x); } } return false; } private void orderByAddColumn(String table, String columnName, SQLObject expr) { Column column = new Column(table, columnName); SQLObject parent = expr.getParent(); if (parent instanceof SQLSelectOrderByItem) { SQLOrderingSpecification type = ((SQLSelectOrderByItem) parent).getType(); column.getAttributes().put("orderBy.type", type); } orderByColumns.add(column); } protected class OrderByStatVisitor extends SQLASTVisitorAdapter { private final SQLOrderBy orderBy; public OrderByStatVisitor(SQLOrderBy orderBy){ this.orderBy = orderBy; for (SQLSelectOrderByItem item : orderBy.getItems()) { item.getExpr().setParent(item); } } public SQLOrderBy getOrderBy() { return orderBy; } public boolean visit(SQLIdentifierExpr x) { return visitOrderBy(x); } public boolean visit(SQLPropertyExpr x) { return visitOrderBy(x); } } protected class MySqlOrderByStatVisitor extends MySqlASTVisitorAdapter { private final SQLOrderBy orderBy; public MySqlOrderByStatVisitor(SQLOrderBy orderBy){ this.orderBy = orderBy; for (SQLSelectOrderByItem item : orderBy.getItems()) { item.getExpr().setParent(item); } } public SQLOrderBy getOrderBy() { return orderBy; } public boolean visit(SQLIdentifierExpr x) { return visitOrderBy(x); } public boolean visit(SQLPropertyExpr x) { return visitOrderBy(x); } } protected class PGOrderByStatVisitor extends PGASTVisitorAdapter { private final SQLOrderBy orderBy; public PGOrderByStatVisitor(SQLOrderBy orderBy){ this.orderBy = orderBy; for (SQLSelectOrderByItem item : orderBy.getItems()) { item.getExpr().setParent(item); } } public SQLOrderBy getOrderBy() { return orderBy; } public boolean visit(SQLIdentifierExpr x) { return visitOrderBy(x); } public boolean visit(SQLPropertyExpr x) { return visitOrderBy(x); } } protected class OracleOrderByStatVisitor extends PGASTVisitorAdapter { private final SQLOrderBy orderBy; public OracleOrderByStatVisitor(SQLOrderBy orderBy){ this.orderBy = orderBy; for (SQLSelectOrderByItem item : orderBy.getItems()) { item.getExpr().setParent(item); } } public SQLOrderBy getOrderBy() { return orderBy; } public boolean visit(SQLIdentifierExpr x) { return visitOrderBy(x); } public boolean visit(SQLPropertyExpr x) { return visitOrderBy(x); } } public boolean visit(SQLOrderBy x) { final SQLASTVisitor orderByVisitor = createOrderByVisitor(x); SQLSelectQueryBlock query = null; if (x.getParent() instanceof SQLSelectQueryBlock) { query = (SQLSelectQueryBlock) x.getParent(); } if (query != null) { for (SQLSelectOrderByItem item : x.getItems()) { SQLExpr expr = item.getExpr(); if (expr instanceof SQLIntegerExpr) { int intValue = ((SQLIntegerExpr) expr).getNumber().intValue() - 1; if (intValue < query.getSelectList().size()) { SQLSelectItem selectItem = query.getSelectList().get(intValue); selectItem.getExpr().accept(orderByVisitor); } } else if (expr instanceof MySqlExpr || expr instanceof OracleExpr) { continue; } } } x.accept(orderByVisitor); return true; } protected SQLASTVisitor createOrderByVisitor(SQLOrderBy x) { final SQLASTVisitor orderByVisitor; if (JdbcConstants.MYSQL.equals(getDbType())) { orderByVisitor = new MySqlOrderByStatVisitor(x); } else if (JdbcConstants.POSTGRESQL.equals(getDbType())) { orderByVisitor = new PGOrderByStatVisitor(x); } else if (JdbcConstants.ORACLE.equals(getDbType())) { orderByVisitor = new OracleOrderByStatVisitor(x); } else { orderByVisitor = new OrderByStatVisitor(x); } return orderByVisitor; } public Set<Relationship> getRelationships() { return relationships; } public List<Column> getOrderByColumns() { return orderByColumns; } public Set<Column> getGroupByColumns() { return groupByColumns; } public List<Condition> getConditions() { return conditions; } public List<SQLAggregateExpr> getAggregateFunctions() { return aggregateFunctions; } public boolean visit(SQLBinaryOpExpr x) { x.getLeft().setParent(x); x.getRight().setParent(x); switch (x.getOperator()) { case Equality: case NotEqual: case GreaterThan: case GreaterThanOrEqual: case LessThan: case LessThanOrGreater: case LessThanOrEqual: case LessThanOrEqualOrGreaterThan: case Like: case NotLike: case Is: case IsNot: handleCondition(x.getLeft(), x.getOperator().name, x.getRight()); handleCondition(x.getRight(), x.getOperator().name, x.getLeft()); handleRelationship(x.getLeft(), x.getOperator().name, x.getRight()); break; default: break; } return true; } protected void handleRelationship(SQLExpr left, String operator, SQLExpr right) { Column leftColumn = getColumn(left); if (leftColumn == null) { return; } Column rightColumn = getColumn(right); if (rightColumn == null) { return; } Relationship relationship = new Relationship(); relationship.setLeft(leftColumn); relationship.setRight(rightColumn); relationship.setOperator(operator); this.relationships.add(relationship); } protected void handleCondition(SQLExpr expr, String operator, List<SQLExpr> values) { handleCondition(expr, operator, values.toArray(new SQLExpr[values.size()])); } protected void handleCondition(SQLExpr expr, String operator, SQLExpr... valueExprs) { if (expr instanceof SQLCastExpr) { expr = ((SQLCastExpr) expr).getExpr(); } Column column = getColumn(expr); if (column == null) { return; } Condition condition = null; for (Condition item : this.getConditions()) { if (item.getColumn().equals(column) && item.getOperator().equals(operator)) { condition = item; break; } } if (condition == null) { condition = new Condition(); condition.setColumn(column); condition.setOperator(operator); this.conditions.add(condition); } for (SQLExpr item : valueExprs) { Column valueColumn = getColumn(item); if (valueColumn != null) { continue; } Object value = SQLEvalVisitorUtils.eval(getDbType(), item, parameters, false); condition.getValues().add(value); } } public String getDbType() { return null; } protected Column getColumn(SQLExpr expr) { Map<String, String> aliasMap = getAliasMap(); if (aliasMap == null) { return null; } if (expr instanceof SQLMethodInvokeExpr) { SQLMethodInvokeExpr methodInvokeExp = (SQLMethodInvokeExpr) expr; if (methodInvokeExp.getParameters().size() == 1) { SQLExpr firstExpr = methodInvokeExp.getParameters().get(0); return getColumn(firstExpr); } } if (expr instanceof SQLCastExpr) { expr = ((SQLCastExpr) expr).getExpr(); } if (expr instanceof SQLPropertyExpr) { SQLExpr owner = ((SQLPropertyExpr) expr).getOwner(); String column = ((SQLPropertyExpr) expr).getName(); if (owner instanceof SQLIdentifierExpr) { String tableName = ((SQLIdentifierExpr) owner).getName(); String table = tableName; String tableNameLower = tableName.toLowerCase(); if (aliasMap.containsKey(tableNameLower)) { table = aliasMap.get(tableNameLower); } if (containsSubQuery(tableNameLower)) { table = null; } if (variants.containsKey(table)) { return null; } if (table != null) { return new Column(table, column); } return handleSubQueryColumn(tableName, column); } return null; } if (expr instanceof SQLIdentifierExpr) { Column attrColumn = (Column) expr.getAttribute(ATTR_COLUMN); if (attrColumn != null) { return attrColumn; } String column = ((SQLIdentifierExpr) expr).getName(); String table = getCurrentTable(); if (table != null && aliasMap.containsKey(table)) { table = aliasMap.get(table); if (table == null) { return null; } } if (table != null) { return new Column(table, column); } if (variants.containsKey(column)) { return null; } return new Column("UNKNOWN", column); } return null; } @Override public boolean visit(SQLTruncateStatement x) { setMode(x, Mode.Delete); setAliasMap(); String originalTable = getCurrentTable(); for (SQLExprTableSource tableSource : x.getTableSources()) { SQLName name = (SQLName) tableSource.getExpr(); String ident = name.toString(); setCurrentTable(ident); x.putAttribute("_old_local_", originalTable); TableStat stat = getTableStat(ident); stat.incrementDeleteCount(); Map<String, String> aliasMap = getAliasMap(); putAliasMap(aliasMap, ident, ident); } return false; } @Override public boolean visit(SQLDropViewStatement x) { setMode(x, Mode.Drop); return true; } @Override public boolean visit(SQLDropTableStatement x) { setMode(x, Mode.Insert); setAliasMap(); String originalTable = getCurrentTable(); for (SQLExprTableSource tableSource : x.getTableSources()) { SQLName name = (SQLName) tableSource.getExpr(); String ident = name.toString(); setCurrentTable(ident); x.putAttribute("_old_local_", originalTable); TableStat stat = getTableStat(ident); stat.incrementDropCount(); Map<String, String> aliasMap = getAliasMap(); putAliasMap(aliasMap, ident, ident); } return false; } @Override public boolean visit(SQLInsertStatement x) { setMode(x, Mode.Insert); setAliasMap(); String originalTable = getCurrentTable(); if (x.getTableName() instanceof SQLName) { String ident = ((SQLName) x.getTableName()).toString(); setCurrentTable(ident); x.putAttribute("_old_local_", originalTable); TableStat stat = getTableStat(ident); stat.incrementInsertCount(); Map<String, String> aliasMap = getAliasMap(); putAliasMap(aliasMap, x.getAlias(), ident); putAliasMap(aliasMap, ident, ident); } accept(x.getColumns()); accept(x.getQuery()); return false; } protected static void putAliasMap(Map<String, String> aliasMap, String name, String value) { if (aliasMap == null || name == null) { return; } aliasMap.put(name.toLowerCase(), value); } protected void accept(SQLObject x) { if (x != null) { x.accept(this); } } protected void accept(List<? extends SQLObject> nodes) { for (int i = 0, size = nodes.size(); i < size; ++i) { accept(nodes.get(i)); } } public boolean visit(SQLSelectQueryBlock x) { if (x.getFrom() == null) { return false; } setMode(x, Mode.Select); if (x.getFrom() instanceof SQLSubqueryTableSource) { x.getFrom().accept(this); return false; } if (x.getInto() != null && x.getInto().getExpr() instanceof SQLName) { SQLName into = (SQLName) x.getInto().getExpr(); String ident = into.toString(); TableStat stat = getTableStat(ident); if (stat != null) { stat.incrementInsertCount(); } } String originalTable = getCurrentTable(); if (x.getFrom() instanceof SQLExprTableSource) { SQLExprTableSource tableSource = (SQLExprTableSource) x.getFrom(); if (tableSource.getExpr() instanceof SQLName) { String ident = tableSource.getExpr().toString(); setCurrentTable(x, ident); x.putAttribute(ATTR_TABLE, ident); if (x.getParent() instanceof SQLSelect) { x.getParent().putAttribute(ATTR_TABLE, ident); } x.putAttribute("_old_local_", originalTable); } } if (x.getFrom() != null) { x.getFrom().accept(this); // 提前执行,获得aliasMap String table = (String) x.getFrom().getAttribute(ATTR_TABLE); if (table != null) { x.putAttribute(ATTR_TABLE, table); } } // String ident = x.getTable().toString(); // // TableStat stat = getTableStat(ident); // stat.incrementInsertCount(); // return false; if (x.getWhere() != null) { x.getWhere().setParent(x); } return true; } public void endVisit(SQLSelectQueryBlock x) { String originalTable = (String) x.getAttribute("_old_local_"); x.putAttribute("table", getCurrentTable()); setCurrentTable(originalTable); setModeOrigin(x); } public boolean visit(SQLJoinTableSource x) { x.getLeft().accept(this); x.getRight().accept(this); SQLExpr condition = x.getCondition(); if (condition != null) { condition.accept(this); } return false; } public boolean visit(SQLPropertyExpr x) { if (x.getOwner() instanceof SQLIdentifierExpr) { String owner = ((SQLIdentifierExpr) x.getOwner()).getName(); if (containsSubQuery(owner)) { return false; } owner = aliasWrap(owner); if (owner != null) { Column column = addColumn(owner, x.getName()); x.putAttribute(ATTR_COLUMN, column); if (column != null) { if (isParentGroupBy(x)) { this.groupByColumns.add(column); } if (column != null) { setColumn(x, column); } } } } return false; } protected String aliasWrap(String name) { Map<String, String> aliasMap = getAliasMap(); if (aliasMap != null) { if (aliasMap.containsKey(name)) { return aliasMap.get(name); } String name_lcase = name.toLowerCase(); if (name_lcase != name && aliasMap.containsKey(name_lcase)) { return aliasMap.get(name_lcase); } // for (Map.Entry<String, String> entry : aliasMap.entrySet()) { // if (entry.getKey() == null) { // continue; // } // if (entry.getKey().equalsIgnoreCase(name)) { // return entry.getValue(); // } // } } return name; } protected Column handleSubQueryColumn(String owner, String alias) { SQLObject query = getSubQuery(owner); if (query == null) { return null; } return handleSubQueryColumn(alias, query); } protected Column handleSubQueryColumn(String alias, SQLObject query) { if (query instanceof SQLSelect) { query = ((SQLSelect) query).getQuery(); } SQLSelectQueryBlock queryBlock = null; List<SQLSelectItem> selectList = null; if (query instanceof SQLSelectQueryBlock) { queryBlock = (SQLSelectQueryBlock) query; if (queryBlock.getGroupBy() != null) { return null; } selectList = queryBlock.getSelectList(); } if (selectList != null) { boolean allColumn = false; String allColumnOwner = null; for (SQLSelectItem item : selectList) { if (!item.getClass().equals(SQLSelectItem.class)) { continue; } String itemAlias = item.getAlias(); SQLExpr itemExpr = item.getExpr(); String ident = itemAlias; if (itemAlias == null) { if (itemExpr instanceof SQLIdentifierExpr) { ident = itemAlias = itemExpr.toString(); } else if (itemExpr instanceof SQLPropertyExpr) { itemAlias = ((SQLPropertyExpr) itemExpr).getName(); ident = itemExpr.toString(); } } if (alias.equalsIgnoreCase(itemAlias)) { Column column = (Column) itemExpr.getAttribute(ATTR_COLUMN); if (column != null) { return column; } else { SQLTableSource from = queryBlock.getFrom(); if (from instanceof SQLSubqueryTableSource) { SQLSelect select = ((SQLSubqueryTableSource) from).getSelect(); Column subQueryColumn = handleSubQueryColumn(ident, select); return subQueryColumn; } } } if (itemExpr instanceof SQLAllColumnExpr) { allColumn = true; } else if (itemExpr instanceof SQLPropertyExpr) { SQLPropertyExpr propertyExpr = (SQLPropertyExpr) itemExpr; if (propertyExpr.getName().equals("*")) { SQLExpr owner = propertyExpr.getOwner(); if (owner instanceof SQLIdentifierExpr) { allColumnOwner = ((SQLIdentifierExpr) owner).getName(); allColumn = true; } } } } if (allColumn) { SQLTableSource from = queryBlock.getFrom(); String tableName = getTable(from, allColumnOwner); if (tableName != null) { return new Column(tableName, alias); } } } return null; } private String getTable(SQLTableSource from, String tableAlias) { if (from instanceof SQLExprTableSource) { boolean aliasEq = StringUtils.equals(from.getAlias(), tableAlias); SQLExpr tableSourceExpr = ((SQLExprTableSource) from).getExpr(); if (tableSourceExpr instanceof SQLName) { String tableName = ((SQLName) tableSourceExpr).toString(); if (tableAlias == null || aliasEq) { return tableName; } } } else if (from instanceof SQLJoinTableSource) { SQLJoinTableSource joinTableSource = (SQLJoinTableSource) from; String leftMatchTableName = getTable(joinTableSource.getLeft(), tableAlias); if (leftMatchTableName != null) { return leftMatchTableName; } return getTable(joinTableSource.getRight(), tableAlias); } return null; } public boolean visit(SQLIdentifierExpr x) { String currentTable = getCurrentTable(); if (containsSubQuery(currentTable)) { return false; } String ident = x.toString(); if (variants.containsKey(ident)) { return false; } if ("LEVEL".equalsIgnoreCase(ident) || "CONNECT_BY_ISCYCLE".equalsIgnoreCase(ident)) { for (SQLObject parent = x.getParent(); parent != null; parent = parent.getParent()) { if (parent instanceof SQLSelectQueryBlock) { SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) parent; if (queryBlock.getStartWith() != null || queryBlock.getConnectBy() != null) { return false; } break; } } } Column column = null; if (currentTable != null) { column = addColumn(currentTable, ident); if (column != null && isParentGroupBy(x)) { this.groupByColumns.add(column); } x.putAttribute(ATTR_COLUMN, column); } else { boolean skip = false; for (SQLObject parent = x.getParent();parent != null;parent = parent.getParent()) { if (parent instanceof SQLSelectQueryBlock) { SQLTableSource from = ((SQLSelectQueryBlock) parent).getFrom(); if (from instanceof OdpsValuesTableSource) { skip = true; break; } } else if (parent instanceof SQLSelectQuery) { break; } } if (!skip) { column = handleUnkownColumn(ident); } if (column != null) { x.putAttribute(ATTR_COLUMN, column); } } if (column != null) { SQLObject parent = x.getParent(); if (parent instanceof SQLPrimaryKey) { column.setPrimaryKey(true); } else if (parent instanceof SQLUnique) { column.setUnique(true); } setColumn(x, column); } return false; } private boolean isParentSelectItem(SQLObject parent) { if (parent == null) { return false; } if (parent instanceof SQLSelectItem) { return true; } if (parent instanceof SQLSelectQueryBlock) { return false; } return isParentSelectItem(parent.getParent()); } private boolean isParentGroupBy(SQLObject parent) { if (parent == null) { return false; } if (parent instanceof SQLSelectGroupByClause) { return true; } return isParentGroupBy(parent.getParent()); } private void setColumn(SQLExpr x, Column column) { SQLObject current = x; for (;;) { SQLObject parent = current.getParent(); if (parent == null) { break; } if (parent instanceof SQLSelectQueryBlock) { SQLSelectQueryBlock query = (SQLSelectQueryBlock) parent; if (query.getWhere() == current) { column.setWhere(true); } break; } if (parent instanceof SQLSelectGroupByClause) { SQLSelectGroupByClause groupBy = (SQLSelectGroupByClause) parent; if (current == groupBy.getHaving()) { column.setHaving(true); } else if (groupBy.getItems().contains(current)) { column.setGroupBy(true); } break; } if (isParentSelectItem(parent)) { column.setSelec(true); break; } if (parent instanceof SQLJoinTableSource) { SQLJoinTableSource join = (SQLJoinTableSource) parent; if (join.getCondition() == current) { column.setJoin(true); } break; } current = parent; } } protected Column handleUnkownColumn(String columnName) { return addColumn("UNKNOWN", columnName); } public boolean visit(SQLAllColumnExpr x) { String currentTable = getCurrentTable(); if (containsSubQuery(currentTable)) { return false; } if (x.getParent() instanceof SQLAggregateExpr) { SQLAggregateExpr aggregateExpr = (SQLAggregateExpr) x.getParent(); if ("count".equalsIgnoreCase(aggregateExpr.getMethodName())) { return false; } } if (currentTable != null) { Column column = addColumn(currentTable, "*"); if (isParentSelectItem(x.getParent())) { column.setSelec(true); } } return false; } public Map<TableStat.Name, TableStat> getTables() { return tableStats; } public boolean containsTable(String tableName) { return tableStats.containsKey(new TableStat.Name(tableName)); } public boolean containsColumn(String tableName, String columnName) { return columns.containsKey(new Column(tableName, columnName)); } public Collection<Column> getColumns() { return columns.values(); } public Column getColumn(String tableName, String columnName) { if (aliasMap != null && aliasMap.containsKey(columnName) && aliasMap.get(columnName) == null) { return null; } Column column = new Column(tableName, columnName); return this.columns.get(column); } public boolean visit(SQLSelectStatement x) { setAliasMap(); return true; } public void endVisit(SQLSelectStatement x) { } @Override public boolean visit(SQLWithSubqueryClause.Entry x) { String alias = x.getName().toString(); Map<String, String> aliasMap = getAliasMap(); SQLWithSubqueryClause with = (SQLWithSubqueryClause) x.getParent(); if (Boolean.TRUE == with.getRecursive()) { if (aliasMap != null && alias != null) { putAliasMap(aliasMap, alias, null); addSubQuery(alias, x.getSubQuery().getQuery()); } x.getSubQuery().accept(this); } else { x.getSubQuery().accept(this); if (aliasMap != null && alias != null) { putAliasMap(aliasMap, alias, null); addSubQuery(alias, x.getSubQuery().getQuery()); } } return false; } public boolean visit(SQLSubqueryTableSource x) { x.getSelect().accept(this); SQLSelectQuery query = x.getSelect().getQuery(); Map<String, String> aliasMap = getAliasMap(); if (aliasMap != null && x.getAlias() != null) { putAliasMap(aliasMap, x.getAlias(), null); addSubQuery(x.getAlias(), query); } return false; } protected void addSubQuery(String alias, SQLObject query) { String alias_lcase = alias.toLowerCase(); subQueryMap.put(alias_lcase, query); } protected SQLObject getSubQuery(String alias) { String alias_lcase = alias.toLowerCase(); return subQueryMap.get(alias_lcase); } protected boolean containsSubQuery(String alias) { if (alias == null) { return false; } String alias_lcase = alias.toLowerCase(); return subQueryMap.containsKey(alias_lcase); } protected boolean isSimpleExprTableSource(SQLExprTableSource x) { return x.getExpr() instanceof SQLName; } public boolean visit(SQLExprTableSource x) { if (isSimpleExprTableSource(x)) { String ident = x.getExpr().toString(); if (variants.containsKey(ident)) { return false; } if (containsSubQuery(ident)) { return false; } Map<String, String> aliasMap = getAliasMap(); TableStat stat = getTableStat(ident); Mode mode = getMode(); if (mode != null) { switch (mode) { case Delete: stat.incrementDeleteCount(); break; case Insert: stat.incrementInsertCount(); break; case Update: stat.incrementUpdateCount(); break; case Select: stat.incrementSelectCount(); break; case Merge: stat.incrementMergeCount(); break; case Drop: stat.incrementDropCount(); break; default: break; } } if (aliasMap != null) { String alias = x.getAlias(); if (alias != null && !aliasMap.containsKey(alias)) { putAliasMap(aliasMap, alias, ident); } if (!aliasMap.containsKey(ident)) { putAliasMap(aliasMap, ident, ident); } } } else { accept(x.getExpr()); } return false; } public boolean visit(SQLSelectItem x) { x.getExpr().accept(this); String alias = x.getAlias(); Map<String, String> aliasMap = this.getAliasMap(); if (alias != null && (!alias.isEmpty()) && aliasMap != null) { if (x.getExpr() instanceof SQLName) { putAliasMap(aliasMap, alias, x.getExpr().toString()); } else { putAliasMap(aliasMap, alias, null); } } return false; } public void endVisit(SQLSelect x) { restoreCurrentTable(x); } public boolean visit(SQLSelect x) { setCurrentTable(x); if (x.getOrderBy() != null) { x.getOrderBy().setParent(x); } accept(x.getWithSubQuery()); accept(x.getQuery()); String originalTable = getCurrentTable(); setCurrentTable((String) x.getQuery().getAttribute("table")); x.putAttribute("_old_local_", originalTable); accept(x.getOrderBy()); setCurrentTable(originalTable); return false; } public boolean visit(SQLAggregateExpr x) { this.aggregateFunctions.add(x); accept(x.getArguments()); accept(x.getWithinGroup()); accept(x.getOver()); return false; } public boolean visit(SQLMethodInvokeExpr x) { this.functions.add(x); accept(x.getParameters()); return false; } public boolean visit(SQLUpdateStatement x) { setAliasMap(); setMode(x, Mode.Update); SQLName identName = x.getTableName(); if (identName != null) { String ident = identName.toString(); setCurrentTable(ident); TableStat stat = getTableStat(ident); stat.incrementUpdateCount(); Map<String, String> aliasMap = getAliasMap(); putAliasMap(aliasMap, ident, ident); } else { x.getTableSource().accept(this); } accept(x.getFrom()); accept(x.getItems()); accept(x.getWhere()); return false; } public boolean visit(SQLDeleteStatement x) { setAliasMap(); setMode(x, Mode.Delete); String tableName = x.getTableName().toString(); setCurrentTable(tableName); if (x.getAlias() != null) { putAliasMap(this.aliasMap, x.getAlias(), tableName); } if (x.getTableSource() instanceof SQLSubqueryTableSource) { SQLSelectQuery selectQuery = ((SQLSubqueryTableSource) x.getTableSource()).getSelect().getQuery(); if (selectQuery instanceof SQLSelectQueryBlock) { SQLSelectQueryBlock subQueryBlock = ((SQLSelectQueryBlock) selectQuery); subQueryBlock.getWhere().accept(this); } } TableStat stat = getTableStat(tableName); stat.incrementDeleteCount(); accept(x.getWhere()); return false; } public boolean visit(SQLInListExpr x) { if (x.isNot()) { handleCondition(x.getExpr(), "NOT IN", x.getTargetList()); } else { handleCondition(x.getExpr(), "IN", x.getTargetList()); } return true; } @Override public boolean visit(SQLInSubQueryExpr x) { if (x.isNot()) { handleCondition(x.getExpr(), "NOT IN"); } else { handleCondition(x.getExpr(), "IN"); } return true; } public void endVisit(SQLDeleteStatement x) { } public void endVisit(SQLUpdateStatement x) { } public boolean visit(SQLCreateTableStatement x) { for (SQLTableElement e : x.getTableElementList()) { e.setParent(x); } String tableName = x.getName().toString(); TableStat stat = getTableStat(tableName); stat.incrementCreateCount(); setCurrentTable(x, tableName); accept(x.getTableElementList()); restoreCurrentTable(x); if (x.getInherits() != null) { x.getInherits().accept(this); } if (x.getSelect() != null) { x.getSelect().accept(this); } return false; } public boolean visit(SQLColumnDefinition x) { String tableName = null; { SQLObject parent = x.getParent(); if (parent instanceof SQLCreateTableStatement) { tableName = ((SQLCreateTableStatement) parent).getName().toString(); } } if (tableName == null) { return true; } String columnName = x.getName().toString(); Column column = addColumn(tableName, columnName); if (x.getDataType() != null) { column.setDataType(x.getDataType().getName()); } for (SQLColumnConstraint item : x.getConstraints()) { if (item instanceof SQLPrimaryKey) { column.setPrimaryKey(true); } else if (item instanceof SQLUnique) { column.setUnique(true); } } return false; } @Override public boolean visit(SQLCallStatement x) { return false; } @Override public void endVisit(SQLCommentStatement x) { } @Override public boolean visit(SQLCommentStatement x) { return false; } public boolean visit(SQLCurrentOfCursorExpr x) { return false; } @Override public boolean visit(SQLAlterTableAddColumn x) { SQLAlterTableStatement stmt = (SQLAlterTableStatement) x.getParent(); String table = stmt.getName().toString(); for (SQLColumnDefinition column : x.getColumns()) { String columnName = column.getName().toString(); addColumn(table, columnName); } return false; } @Override public void endVisit(SQLAlterTableAddColumn x) { } @Override public boolean visit(SQLRollbackStatement x) { return false; } public boolean visit(SQLCreateViewStatement x) { x.getSubQuery().accept(this); return false; } @Override public boolean visit(SQLAlterTableDropForeignKey x) { return false; } @Override public boolean visit(SQLUseStatement x) { return false; } @Override public boolean visit(SQLAlterTableDisableConstraint x) { return false; } @Override public boolean visit(SQLAlterTableEnableConstraint x) { return false; } @Override public boolean visit(SQLAlterTableStatement x) { String tableName = x.getName().toString(); TableStat stat = getTableStat(tableName); stat.incrementAlterCount(); setCurrentTable(x, tableName); for (SQLAlterTableItem item : x.getItems()) { item.setParent(x); item.accept(this); } return false; } @Override public boolean visit(SQLAlterTableDropConstraint x) { return false; } @Override public boolean visit(SQLDropIndexStatement x) { setMode(x, Mode.DropIndex); SQLExprTableSource table = x.getTableName(); if (table != null) { SQLName name = (SQLName) table.getExpr(); String ident = name.toString(); setCurrentTable(ident); TableStat stat = getTableStat(ident); stat.incrementDropIndexCount(); Map<String, String> aliasMap = getAliasMap(); putAliasMap(aliasMap, ident, ident); } return false; } @Override public boolean visit(SQLCreateIndexStatement x) { setMode(x, Mode.CreateIndex); SQLName name = (SQLName) ((SQLExprTableSource) x.getTable()).getExpr(); String table = name.toString(); setCurrentTable(table); TableStat stat = getTableStat(table); stat.incrementDropIndexCount(); Map<String, String> aliasMap = getAliasMap(); putAliasMap(aliasMap, table, table); for (SQLSelectOrderByItem item : x.getItems()) { SQLExpr expr = item.getExpr(); if (expr instanceof SQLIdentifierExpr) { SQLIdentifierExpr identExpr = (SQLIdentifierExpr) expr; String columnName = identExpr.getName(); addColumn(table, columnName); } } return false; } @Override public boolean visit(SQLForeignKeyImpl x) { for (SQLName column : x.getReferencingColumns()) { column.accept(this); } String table = x.getReferencedTableName().getSimpleName(); setCurrentTable(table); TableStat stat = getTableStat(table); stat.incrementReferencedCount(); for (SQLName column : x.getReferencedColumns()) { String columnName = column.getSimpleName(); addColumn(table, columnName); } return false; } @Override public boolean visit(SQLDropSequenceStatement x) { return false; } @Override public boolean visit(SQLDropTriggerStatement x) { return false; } @Override public boolean visit(SQLDropUserStatement x) { return false; } @Override public boolean visit(SQLGrantStatement x) { if (x.getOn() != null && (x.getObjectType() == null || x.getObjectType() == SQLObjectType.TABLE)) { x.getOn().accept(this); } return false; } @Override public boolean visit(SQLRevokeStatement x) { if (x.getOn() != null) { x.getOn().accept(this); } return false; } @Override public boolean visit(SQLDropDatabaseStatement x) { return false; } @Override public boolean visit(SQLAlterTableAddIndex x) { for (SQLSelectOrderByItem item : x.getItems()) { item.accept(this); } String table = ((SQLAlterTableStatement) x.getParent()).getName().toString(); TableStat tableStat = this.getTableStat(table); tableStat.incrementCreateIndexCount(); return false; } public boolean visit(SQLCheck x) { x.getExpr().accept(this); return false; } public boolean visit(SQLCreateTriggerStatement x) { return false; } public boolean visit(SQLDropFunctionStatement x) { return false; } public boolean visit(SQLDropTableSpaceStatement x) { return false; } public boolean visit(SQLDropProcedureStatement x) { return false; } @Override public boolean visit(SQLAlterTableRename x) { return false; } @Override public boolean visit(SQLArrayExpr x) { accept(x.getValues()); SQLExpr exp = x.getExpr(); if (exp instanceof SQLIdentifierExpr) { if (((SQLIdentifierExpr) exp).getName().equals("ARRAY")) { return false; } } exp.accept(this); return false; } @Override public boolean visit(SQLOpenStatement x) { return false; } @Override public boolean visit(SQLFetchStatement x) { return false; } @Override public boolean visit(SQLCloseStatement x) { return false; } @Override public boolean visit(SQLCreateProcedureStatement x) { String name = x.getName().toString(); this.variants.put(name, x); accept(x.getBlock()); return false; } @Override public boolean visit(SQLBlockStatement x) { for (SQLParameter param : x.getParameters()) { param.setParent(x); SQLExpr name = param.getName(); this.variants.put(name.toString(), name); } return true; } @Override public boolean visit(SQLShowTablesStatement x) { return false; } @Override public boolean visit(SQLDeclareItem x) { return false; } @Override public boolean visit(SQLPartitionByHash x) { return false; } @Override public boolean visit(SQLPartitionByRange x) { return false; } @Override public boolean visit(SQLPartitionByList x) { return false; } @Override public boolean visit(SQLPartition x) { return false; } @Override public boolean visit(SQLSubPartition x) { return false; } @Override public boolean visit(SQLSubPartitionByHash x) { return false; } @Override public boolean visit(SQLPartitionValue x) { return false; } @Override public boolean visit(SQLAlterDatabaseStatement x) { return true; } @Override public boolean visit(SQLAlterTableConvertCharSet x) { return false; } @Override public boolean visit(SQLAlterTableDropPartition x) { return false; } @Override public boolean visit(SQLAlterTableReOrganizePartition x) { return false; } @Override public boolean visit(SQLAlterTableCoalescePartition x) { return false; } @Override public boolean visit(SQLAlterTableTruncatePartition x) { return false; } @Override public boolean visit(SQLAlterTableDiscardPartition x) { return false; } @Override public boolean visit(SQLAlterTableImportPartition x) { return false; } @Override public boolean visit(SQLAlterTableAnalyzePartition x) { return false; } @Override public boolean visit(SQLAlterTableCheckPartition x) { return false; } @Override public boolean visit(SQLAlterTableOptimizePartition x) { return false; } @Override public boolean visit(SQLAlterTableRebuildPartition x) { return false; } @Override public boolean visit(SQLAlterTableRepairPartition x) { return false; } public boolean visit(SQLSequenceExpr x) { return false; } @Override public boolean visit(SQLMergeStatement x) { setAliasMap(); String originalTable = getCurrentTable(); setMode(x.getUsing(), Mode.Select); x.getUsing().accept(this); setMode(x, Mode.Merge); String ident = x.getInto().toString(); setCurrentTable(x, ident); x.putAttribute("_old_local_", originalTable); TableStat stat = getTableStat(ident); stat.incrementMergeCount(); Map<String, String> aliasMap = getAliasMap(); if (aliasMap != null) { if (x.getAlias() != null) { putAliasMap(aliasMap, x.getAlias(), ident); } putAliasMap(aliasMap, ident, ident); } x.getOn().accept(this); if (x.getUpdateClause() != null) { x.getUpdateClause().accept(this); } if (x.getInsertClause() != null) { x.getInsertClause().accept(this); } return false; } @Override public boolean visit(SQLSetStatement x) { return false; } public List<SQLMethodInvokeExpr> getFunctions() { return this.functions; } public boolean visit(SQLCreateSequenceStatement x) { return false; } @Override public boolean visit(SQLAlterTableAddConstraint x) { SQLConstraint constraint = x.getConstraint(); if (constraint instanceof SQLUniqueConstraint) { SQLAlterTableStatement stmt = (SQLAlterTableStatement) x.getParent(); String tableName = stmt.getName().toString(); TableStat tableStat = this.getTableStat(tableName); tableStat.incrementCreateIndexCount(); } return true; } @Override public boolean visit(SQLAlterTableDropIndex x) { SQLAlterTableStatement stmt = (SQLAlterTableStatement) x.getParent(); String tableName = stmt.getName().toString(); TableStat tableStat = this.getTableStat(tableName); tableStat.incrementDropIndexCount(); return false; } @Override public boolean visit(SQLAlterTableDropPrimaryKey x) { SQLAlterTableStatement stmt = (SQLAlterTableStatement) x.getParent(); String tableName = stmt.getName().toString(); TableStat tableStat = this.getTableStat(tableName); tableStat.incrementDropIndexCount(); return false; } @Override public boolean visit(SQLAlterTableDropKey x) { SQLAlterTableStatement stmt = (SQLAlterTableStatement) x.getParent(); String tableName = stmt.getName().toString(); TableStat tableStat = this.getTableStat(tableName); tableStat.incrementDropIndexCount(); return false; } @Override public boolean visit(SQLDescribeStatement x) { String tableName = x.getObject().toString(); TableStat tableStat = this.getTableStat(tableName); tableStat.incrementDropIndexCount(); SQLName column = x.getColumn(); if (column != null) { String columnName = column.toString(); this.addColumn(tableName, columnName); } return false; } public boolean visit(SQLExplainStatement x) { if (x.getStatement() != null) { accept(x.getStatement()); } return false; } // // }