diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java index 5e4df905e8..70ba0d10b7 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java @@ -27,7 +27,6 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUnionOperator; import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; -import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; import com.alibaba.druid.sql.dialect.oracle.ast.clause.CycleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.AsOfFlashbackQueryClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.VersionsFlashbackQueryClause; diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java index d047798b01..9522bc91a4 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java @@ -74,7 +74,6 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleUpdateSetValueClause; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleUpdateStatement; import com.alibaba.druid.sql.visitor.SchemaStatVisitor; import com.alibaba.druid.stat.TableStat; -import com.alibaba.druid.stat.TableStat.Column; import com.alibaba.druid.stat.TableStat.Mode; public class OracleSchemaStatVisitor extends SchemaStatVisitor implements OracleASTVisitor { @@ -110,6 +109,9 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle case Select: stat.incrementSelectCount(); break; + case Merge: + stat.incrementMergeCount(); + break; default: break; } @@ -216,22 +218,7 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle return false; } - if (x.getOwner() instanceof SQLIdentifierExpr) { - String owner = ((SQLIdentifierExpr) x.getOwner()).getName(); - - if (owner != null) { - Map aliasMap = aliasLocal.get(); - if (aliasMap != null) { - String table = aliasMap.get(owner); - - // table == null时是SubQuery - if (table != null) { - columns.add(new Column(table, x.getName())); - } - } - } - } - return false; + return super.visit(x); } public boolean visit(SQLIdentifierExpr x) { @@ -247,12 +234,7 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle return false; } - String currentTable = currentTableLocal.get(); - - if (currentTable != null) { - columns.add(new Column(currentTable, x.getName())); - } - return false; + return super.visit(x); } @Override @@ -826,12 +808,50 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle @Override public boolean visit(OracleMergeStatement x) { - return true; + aliasLocal.set(new HashMap()); + + x.putAttribute("_original_use_mode", modeLocal.get()); + modeLocal.set(Mode.Merge); + + String originalTable = currentTableLocal.get(); + + x.getUsing().accept(this); + + String ident = x.getInto().toString(); + currentTableLocal.set(ident); + x.putAttribute("_old_local_", originalTable); + + TableStat stat = tableStats.get(ident); + if (stat == null) { + stat = new TableStat(); + tableStats.put(new TableStat.Name(ident), stat); + } + stat.incrementMergeCount(); + + Map aliasMap = aliasLocal.get(); + if (aliasMap != null) { + if (x.getAlias() != null) { + aliasMap.put(x.getAlias(), ident); + } + aliasMap.put(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 void endVisit(OracleMergeStatement x) { - + aliasLocal.set(null); } @Override @@ -841,7 +861,7 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle @Override public void endVisit(MergeUpdateClause x) { - + } @Override @@ -851,7 +871,7 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle @Override public void endVisit(MergeInsertClause x) { - + } @Override @@ -861,7 +881,8 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle @Override public void endVisit(ErrorLoggingClause x) { - + + } } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java index 575334d787..f32d91e206 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java @@ -28,6 +28,7 @@ import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource; import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectItem; import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem; +import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; @@ -37,15 +38,18 @@ 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; public class SchemaStatVisitor extends SQLASTVisitorAdapter { protected final HashMap tableStats = new LinkedHashMap(); protected final Set columns = new LinkedHashSet(); protected final Set conditions = new LinkedHashSet(); + protected final Set relationships = new LinkedHashSet(); protected final Set orderByColumns = new LinkedHashSet(); protected final Set groupByColumns = new LinkedHashSet(); + private final Map subQueryMap = new LinkedHashMap(); protected final static ThreadLocal> aliasLocal = new ThreadLocal>(); protected final static ThreadLocal currentTableLocal = new ThreadLocal(); protected final static ThreadLocal modeLocal = new ThreadLocal(); @@ -114,6 +118,10 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter { return true; } + public Set getRelationships() { + return relationships; + } + public Set getOrderByColumns() { return orderByColumns; } @@ -141,6 +149,8 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter { case IsNot: handleCondition(x.getLeft(), x.getOperator().name); handleCondition(x.getRight(), x.getOperator().name); + + handleRelationship(x.getLeft(), x.getOperator().name, x.getRight()); break; default: break; @@ -148,10 +158,41 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter { 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) { + Column column = getColumn(expr); + if (column == null) { + return; + } + + Condition condition = new Condition(); + condition.setColumn(column); + condition.setOperator(operator); + this.conditions.add(condition); + } + + protected Column getColumn(SQLExpr expr) { Map aliasMap = aliasLocal.get(); if (aliasMap == null) { - return; + return null; } if (expr instanceof SQLPropertyExpr) { @@ -159,20 +200,20 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter { String column = ((SQLPropertyExpr) expr).getName(); if (owner instanceof SQLIdentifierExpr) { - String table = ((SQLIdentifierExpr) owner).getName(); + String tableName = ((SQLIdentifierExpr) owner).getName(); + String table = tableName; if (aliasMap.containsKey(table)) { table = aliasMap.get(table); } if (table != null) { - Condition condition = new Condition(); - condition.setColumn(new Column(table, column)); - condition.setOperator(operator); - this.conditions.add(condition); + return new Column(table, column); } + + return handleSubQueryColumn(tableName, column); } - return; + return null; } if (expr instanceof SQLIdentifierExpr) { @@ -185,12 +226,11 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter { } if (table != null) { - Condition condition = new Condition(); - condition.setColumn(new Column(table, column)); - condition.setOperator(operator); - this.conditions.add(condition); + return new Column(table, column); } } + + return null; } @Override @@ -357,7 +397,9 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter { // table == null时是SubQuery if (table != null) { - columns.add(new Column(table, x.getName())); + Column column = new Column(table, x.getName()); + columns.add(column); + x.putAttribute("_column_", column); } } } @@ -365,11 +407,48 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter { return false; } + protected Column handleSubQueryColumn(String owner, String alias) { + SQLSelectQuery query = subQueryMap.get(owner); + + if (query == null) { + return null; + } + + List selectList = null; + if (query instanceof SQLSelectQueryBlock) { + selectList = ((SQLSelectQueryBlock) query).getSelectList(); + } + + if (selectList != null) { + for (SQLSelectItem item : selectList) { + String itemAlias = item.getAlias(); + SQLExpr itemExpr = item.getExpr(); + if (itemAlias == null) { + if (itemExpr instanceof SQLIdentifierExpr) { + itemAlias = itemExpr.toString(); + } else if (itemExpr instanceof SQLPropertyExpr) { + itemAlias = ((SQLPropertyExpr) itemExpr).getName(); + } + } + + if (alias.equalsIgnoreCase(itemAlias)) { + Column column = (Column) itemExpr.getAttribute("_column_"); + return column; + } + + } + } + + return null; + } + public boolean visit(SQLIdentifierExpr x) { String currentTable = currentTableLocal.get(); if (currentTable != null) { - columns.add(new Column(currentTable, x.getName())); + Column column = new Column(currentTable, x.getName()); + columns.add(column); + x.putAttribute("_column_", column); } return false; } @@ -405,13 +484,18 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter { } public boolean visit(SQLSubqueryTableSource x) { + x.getSelect().accept(this); + + SQLSelectQuery query = x.getSelect().getQuery(); + Map aliasMap = aliasLocal.get(); if (aliasMap != null) { if (x.getAlias() != null) { aliasMap.put(x.getAlias(), null); + subQueryMap.put(x.getAlias(), query); } } - return true; + return false; } protected boolean isSimpleExprTableSource(SQLExprTableSource x) { @@ -448,6 +532,9 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter { case Select: stat.incrementSelectCount(); break; + case Merge: + stat.incrementMergeCount(); + break; default: break; } @@ -465,6 +552,11 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter { return false; } + public boolean visit(SQLSelectItem x) { + x.getExpr().setParent(x); + return true; + } + public boolean visit(SQLSelect x) { if (x.getOrderBy() != null) { x.getOrderBy().setParent(x); diff --git a/src/main/java/com/alibaba/druid/stat/TableStat.java b/src/main/java/com/alibaba/druid/stat/TableStat.java index c8aeb099b8..25c7ade638 100644 --- a/src/main/java/com/alibaba/druid/stat/TableStat.java +++ b/src/main/java/com/alibaba/druid/stat/TableStat.java @@ -22,6 +22,15 @@ public class TableStat { int deleteCount = 0; int insertCount = 0; int dropCount = 0; + int mergeCount = 0; + + public int getMergeCount() { + return mergeCount; + } + + public void incrementMergeCount() { + this.mergeCount++; + } public int getDropCount() { return dropCount; @@ -85,6 +94,9 @@ public class TableStat { public String toString() { StringBuilder buf = new StringBuilder(4); + if (mergeCount > 0) { + buf.append("Merge"); + } if (insertCount > 0) { buf.append("Insert"); } @@ -135,6 +147,89 @@ public class TableStat { } } + public static class Relationship { + + private Column left; + private Column right; + private String operator; + + public Column getLeft() { + return left; + } + + public void setLeft(Column left) { + this.left = left; + } + + public Column getRight() { + return right; + } + + public void setRight(Column right) { + this.right = right; + } + + public String getOperator() { + return operator; + } + + public void setOperator(String operator) { + this.operator = operator; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((left == null) ? 0 : left.hashCode()); + result = prime * result + ((operator == null) ? 0 : operator.hashCode()); + result = prime * result + ((right == null) ? 0 : right.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Relationship other = (Relationship) obj; + if (left == null) { + if (other.left != null) { + return false; + } + } else if (!left.equals(other.left)) { + return false; + } + if (operator == null) { + if (other.operator != null) { + return false; + } + } else if (!operator.equals(other.operator)) { + return false; + } + if (right == null) { + if (other.right != null) { + return false; + } + } else if (!right.equals(other.right)) { + return false; + } + return true; + } + + @Override + public String toString() { + return left + " " + operator + " " + right; + } + + } + public static class Condition { private Column column; @@ -272,7 +367,7 @@ public class TableStat { } public static enum Mode { - Insert(1), Update(2), Delete(4), Select(8); + Insert(1), Update(2), Delete(4), Select(8), Merge(16); public final int mark; diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMergeTest.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMergeTest.java index 5f099f8075..ff6eb8ba30 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMergeTest.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMergeTest.java @@ -36,15 +36,20 @@ public class OracleMergeTest extends OracleTest { System.out.println("Tables : " + visitor.getTables()); System.out.println("fields : " + visitor.getColumns()); System.out.println("coditions : " + visitor.getConditions()); + System.out.println("relationships : " + visitor.getRelationships()); Assert.assertEquals(2, visitor.getTables().size()); Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("employees"))); - Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("departments"))); + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("bonuses"))); - Assert.assertEquals(4, visitor.getColumns().size()); + Assert.assertEquals(5, visitor.getColumns().size()); - // Assert.assertTrue(visitor.getFields().contains(new TableStat.Column("films", "producer_id"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("employees", "employee_id"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("employees", "salary"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("employees", "department_id"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("bonuses", "employee_id"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("bonuses", "bonus"))); } }