diff --git a/src/main/java/com/alibaba/druid/mapping/MappingEngine.java b/src/main/java/com/alibaba/druid/mapping/MappingEngine.java index b3567dc234..8257f4c5e9 100644 --- a/src/main/java/com/alibaba/druid/mapping/MappingEngine.java +++ b/src/main/java/com/alibaba/druid/mapping/MappingEngine.java @@ -7,6 +7,7 @@ import com.alibaba.druid.sql.ast.expr.SQLNumberExpr; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock.Limit; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlSelectParser; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class MappingEngine { @@ -29,13 +30,22 @@ public class MappingEngine { this.entities.put(entity.getName(), entity); } - public MySqlMappingVisitor createMappingVisitor() { + public SQLASTVisitor createMySqlMappingVisitor() { return new MySqlMappingVisitor(entities); } - public String explain(String sql) { - MySqlSelectQueryBlock query = explainToSQLObject(sql); + public String explainToMySql(String sql) { + MySqlSelectQueryBlock query = explainToMySqlObject(sql); + query.accept(this.createMySqlMappingVisitor()); + + return SQLUtils.toMySqlString(query); + } + + public MySqlSelectQueryBlock explainToMySqlObject(String sql) { + MySqlSelectParser selectParser = new MySqlSelectParser(sql); + MySqlSelectQueryBlock query = (MySqlSelectQueryBlock) selectParser.query(); + if (this.maxLimit != null) { if (query.getLimit() == null) { Limit limit = new Limit(); @@ -49,15 +59,7 @@ public class MappingEngine { } } } - - query.accept(this.createMappingVisitor()); - - return SQLUtils.toMySqlString(query); - } - - public MySqlSelectQueryBlock explainToSQLObject(String sql) { - MySqlSelectParser selectParser = new MySqlSelectParser(sql); - MySqlSelectQueryBlock query = (MySqlSelectQueryBlock) selectParser.query(); + return query; } } diff --git a/src/main/java/com/alibaba/druid/mapping/MappingVisitor.java b/src/main/java/com/alibaba/druid/mapping/MappingVisitor.java new file mode 100644 index 0000000000..4436c3e296 --- /dev/null +++ b/src/main/java/com/alibaba/druid/mapping/MappingVisitor.java @@ -0,0 +1,15 @@ +package com.alibaba.druid.mapping; + +import java.util.Map; + +import com.alibaba.druid.sql.ast.statement.SQLTableSource; + +public interface MappingVisitor { + Entity getFirstEntity(); + + Entity getEntity(String name); + + Map getEntities(); + + Map getTableSources(); +} diff --git a/src/main/java/com/alibaba/druid/mapping/MappingVisitorUtils.java b/src/main/java/com/alibaba/druid/mapping/MappingVisitorUtils.java new file mode 100644 index 0000000000..28acc3d4be --- /dev/null +++ b/src/main/java/com/alibaba/druid/mapping/MappingVisitorUtils.java @@ -0,0 +1,103 @@ +package com.alibaba.druid.mapping; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.ast.statement.SQLSelectItem; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.ast.statement.SQLTableSource; + +public class MappingVisitorUtils { + public static boolean visit(MappingVisitor visitor, SQLExprTableSource x) { + SQLExpr expr = x.getExpr(); + + if (expr instanceof SQLIdentifierExpr) { + SQLIdentifierExpr tableExpr = (SQLIdentifierExpr) expr; + String entityName = tableExpr.getName(); + + Entity entity = visitor.getEntity(entityName); + + if (entity == null) { + throw new DruidMappingException("entity not foudn : " + entityName); + } + + tableExpr.setName(entity.getTableName()); + } + + if (x.getAlias() != null) { + visitor.getTableSources().put(x.getAlias(), x); + } + + return false; + } + + public static boolean visit(MappingVisitor visitor, SQLTableSource x) { + if (x.getAlias() != null) { + visitor.getTableSources().put(x.getAlias(), x); + } + + return true; + } + + public static void fillSelectList(MappingVisitor visitor, SQLSelectQueryBlock x) { + Entity entity = visitor.getFirstEntity(); + + for (Property item : entity.getProperties().values()) { + x.getSelectList().add(new SQLSelectItem(new SQLIdentifierExpr(item.getName()), '"' + item.getName() + '"')); + } + } + + public static boolean visit(MappingVisitor visitor, SQLIdentifierExpr x) { + String propertyName = x.getName(); + + Property property = null; + for (Entity entity : visitor.getEntities().values()) { + property = entity.getProperty(propertyName); + if (property != null) { + break; + } + } + + if (property == null) { + throw new DruidMappingException("property not found : " + propertyName); + } + + String dbColumName = property.getDbColumnName(); + x.setName(dbColumName); + + if (x.getParent() instanceof SQLSelectItem) { + SQLSelectItem selectItem = (SQLSelectItem) x.getParent(); + if (selectItem.getAlias() == null) { + selectItem.setAlias('"' + property.getName() + '"'); + } + } + + return false; + } + + public static boolean visit(MappingVisitor visitor, SQLSelectQueryBlock x) { + if (x.getSelectList().size() == 0) { + fillSelectList(visitor, x); + } + + if (x.getSelectList().size() == 1) { + if (x.getSelectList().get(0).getExpr() instanceof SQLAllColumnExpr) { + x.getSelectList().clear(); + fillSelectList(visitor, x); + } + } + + if (x.getFrom() == null) { + Entity firstEntity = visitor.getFirstEntity(); + SQLExprTableSource from = new SQLExprTableSource(new SQLIdentifierExpr(firstEntity.getName())); + x.setFrom(from); + } + + for (SQLSelectItem item : x.getSelectList()) { + item.setParent(x); + } + + return true; + } +} diff --git a/src/main/java/com/alibaba/druid/mapping/MySqlMappingVisitor.java b/src/main/java/com/alibaba/druid/mapping/MySqlMappingVisitor.java index 9f4faa1c0a..25eacdc031 100644 --- a/src/main/java/com/alibaba/druid/mapping/MySqlMappingVisitor.java +++ b/src/main/java/com/alibaba/druid/mapping/MySqlMappingVisitor.java @@ -3,23 +3,29 @@ package com.alibaba.druid.mapping; import java.util.LinkedHashMap; import java.util.Map; -import com.alibaba.druid.sql.ast.SQLExpr; -import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource; import com.alibaba.druid.sql.ast.statement.SQLSelectItem; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; +import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter; -public class MySqlMappingVisitor extends MySqlASTVisitorAdapter { +public class MySqlMappingVisitor extends MySqlASTVisitorAdapter implements MappingVisitor { private final LinkedHashMap entities; + private final Map tableSources = new LinkedHashMap(); public MySqlMappingVisitor(LinkedHashMap entities){ this.entities = entities; } + public Map getTableSources() { + return tableSources; + } + public Map getEntities() { return entities; } @@ -53,98 +59,30 @@ public class MySqlMappingVisitor extends MySqlASTVisitorAdapter { } public boolean visit(MySqlSelectQueryBlock x) { - if (x.getSelectList().size() == 0) { - fillSelectList(x); - } - - if (x.getSelectList().size() == 1) { - if (x.getSelectList().get(0).getExpr() instanceof SQLAllColumnExpr) { - x.getSelectList().clear(); - fillSelectList(x); - } - } - - if (x.getFrom() == null) { - Entity firstEntity = this.getFirstEntity(); - SQLExprTableSource from = new SQLExprTableSource(new SQLIdentifierExpr(firstEntity.getName())); - x.setFrom(from); - } - - for (SQLSelectItem item : x.getSelectList()) { - item.setParent(x); - } - - return super.visit(x); + return MappingVisitorUtils.visit(this, x); } public boolean visit(SQLSelectQueryBlock x) { - if (x instanceof MySqlSelectQueryBlock) { - return visit((MySqlSelectQueryBlock) x); - } - - if (x.getSelectList().size() == 0) { - fillSelectList(x); - } - - for (SQLSelectItem item : x.getSelectList()) { - item.setParent(x); - } - - return super.visit(x); + return MappingVisitorUtils.visit(this, x); } protected void fillSelectList(SQLSelectQueryBlock x) { - Entity entity = getFirstEntity(); - - for (Property item : entity.getProperties().values()) { - x.getSelectList().add(new SQLSelectItem(new SQLIdentifierExpr(item.getName()), '"' + item.getName() + '"')); - } + MappingVisitorUtils.fillSelectList(this, x); } public boolean visit(SQLIdentifierExpr x) { - String propertyName = x.getName(); + return MappingVisitorUtils.visit(this, x); + } - Property property = null; - for (Entity entity : this.entities.values()) { - property = entity.getProperty(propertyName); - if (property != null) { - break; - } - } + public boolean visit(SQLSubqueryTableSource x) { + return MappingVisitorUtils.visit(this, x); + } - if (property == null) { - throw new DruidMappingException("property not found : " + propertyName); - } - - String dbColumName = property.getDbColumnName(); - x.setName(dbColumName); - - if (x.getParent() instanceof SQLSelectItem) { - SQLSelectItem selectItem = (SQLSelectItem) x.getParent(); - if (selectItem.getAlias() == null) { - selectItem.setAlias('"' + property.getName() + '"'); - } - } - - return false; + public boolean visit(SQLJoinTableSource x) { + return MappingVisitorUtils.visit(this, x); } public boolean visit(SQLExprTableSource x) { - SQLExpr expr = x.getExpr(); - - if (expr instanceof SQLIdentifierExpr) { - SQLIdentifierExpr tableExpr = (SQLIdentifierExpr) expr; - String entityName = tableExpr.getName(); - - Entity entity = this.getEntity(entityName); - - if (entity == null) { - throw new DruidMappingException("entity not foudn : " + entityName); - } - - tableExpr.setName(entity.getTableName()); - } - - return false; + return MappingVisitorUtils.visit(this, x); } } diff --git a/src/main/java/com/alibaba/druid/mapping/OracleMappingVisitor.java b/src/main/java/com/alibaba/druid/mapping/OracleMappingVisitor.java new file mode 100644 index 0000000000..dd09b3482e --- /dev/null +++ b/src/main/java/com/alibaba/druid/mapping/OracleMappingVisitor.java @@ -0,0 +1,93 @@ +package com.alibaba.druid.mapping; + +import java.util.LinkedHashMap; +import java.util.Map; + +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource; +import com.alibaba.druid.sql.ast.statement.SQLSelectItem; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; +import com.alibaba.druid.sql.ast.statement.SQLTableSource; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectTableReference; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVIsitorAdapter; + +public class OracleMappingVisitor extends OracleASTVIsitorAdapter implements MappingVisitor { + private final LinkedHashMap entities; + private final Map tableSources = new LinkedHashMap(); + + public OracleMappingVisitor(LinkedHashMap entities) { + super(); + this.entities = entities; + } + + public LinkedHashMap getEntities() { + return entities; + } + + public Map getTableSources() { + return tableSources; + } + + public Entity getFirstEntity() { + for (Map.Entry entry : entities.entrySet()) { + return entry.getValue(); + } + + return null; + } + + public Entity getEntity(String name) { + Entity entity = this.entities.get(name); + + if (entity == null) { + for (Map.Entry entry : entities.entrySet()) { + if (entry.getKey().equalsIgnoreCase(name)) { + entity = entry.getValue(); + break; + } + } + } + + return entity; + } + + public boolean visit(SQLSelectItem x) { + x.getExpr().setParent(x); + return true; + } + + public boolean visit(OracleSelectQueryBlock x) { + return MappingVisitorUtils.visit(this, x); + } + + public boolean visit(SQLSelectQueryBlock x) { + return MappingVisitorUtils.visit(this, x); + } + + protected void fillSelectList(SQLSelectQueryBlock x) { + MappingVisitorUtils.fillSelectList(this, x); + } + + public boolean visit(SQLIdentifierExpr x) { + return MappingVisitorUtils.visit(this, x); + } + + public boolean visit(SQLSubqueryTableSource x) { + return MappingVisitorUtils.visit(this, x); + } + + public boolean visit(SQLJoinTableSource x) { + return MappingVisitorUtils.visit(this, x); + } + + public boolean visit(SQLExprTableSource x) { + return MappingVisitorUtils.visit(this, x); + } + + public boolean visit(OracleSelectTableReference x) { + return MappingVisitorUtils.visit(this, x); + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java index 12e382ca59..b3da9c8ec4 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java @@ -187,4 +187,8 @@ public interface MySqlASTVisitor extends SQLASTVisitor { void endVisit(MySqlPartitionByKey x); boolean visit(MySqlPartitionByKey x); + + boolean visit(MySqlSelectQueryBlock x); + + void endVisit(MySqlSelectQueryBlock x); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java index 87cd27f764..c247564bf3 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java @@ -26,6 +26,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlReplicateStatement import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlResetStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlRollbackStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectGroupBy; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock.Limit; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowColumnsStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowDatabasesStatement; @@ -400,4 +401,14 @@ public class MySqlASTVisitorAdapter extends SQLASTVisitorAdapter implements return true; } + @Override + public boolean visit(MySqlSelectQueryBlock x) { + return true; + } + + @Override + public void endVisit(MySqlSelectQueryBlock x) { + + } + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java index 3395ff53c9..635045529c 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java @@ -1343,4 +1343,9 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV } return false; } + + @Override + public void endVisit(MySqlSelectQueryBlock x) { + + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java index 4e4f72c3f3..91fa856636 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java @@ -5,6 +5,7 @@ import java.util.Map; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.statement.SQLDropTableStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlKey; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey; @@ -32,6 +33,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlReplicateStatement import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlResetStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlRollbackStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectGroupBy; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock.Limit; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowColumnsStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowDatabasesStatement; @@ -49,10 +51,11 @@ public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlAS public boolean visit(SQLSelectStatement x) { setAliasMap(); getAliasMap().put("DUAL", null); - + return true; } - //DUAL + + // DUAL public boolean visit(MySqlDeleteStatement x) { setAliasMap(); @@ -426,31 +429,45 @@ public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlAS public boolean visit(UserSpecification x) { return true; } - + @Override public void endVisit(MySqlDropUser x) { - + } - + @Override public boolean visit(MySqlDropUser x) { return true; } + @Override public void endVisit(MySqlDropTableStatement x) { - + } + @Override public boolean visit(MySqlDropTableStatement x) { return visit((SQLDropTableStatement) x); } + @Override public void endVisit(MySqlPartitionByKey x) { - + } + @Override public boolean visit(MySqlPartitionByKey x) { accept(x.getColumns()); return false; } + + @Override + public boolean visit(MySqlSelectQueryBlock x) { + return this.visit((SQLSelectQueryBlock) x); + } + + @Override + public void endVisit(MySqlSelectQueryBlock x) { + + } } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java index 6e41d7fda7..58610c2288 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java @@ -198,7 +198,7 @@ public interface SQLASTVisitor { boolean visit(SQLSelectQueryBlock x); void endVisit(SQLSelectQueryBlock x); - + boolean visit(SQLExprTableSource x); void endVisit(SQLExprTableSource x); diff --git a/src/test/java/com/alibaba/druid/bvt/mapping/MappingVisitorTest.java b/src/test/java/com/alibaba/druid/bvt/mapping/MappingVisitorTest.java index 2fce815d43..7b90d06e3a 100644 --- a/src/test/java/com/alibaba/druid/bvt/mapping/MappingVisitorTest.java +++ b/src/test/java/com/alibaba/druid/bvt/mapping/MappingVisitorTest.java @@ -26,18 +26,18 @@ public class MappingVisitorTest extends TestCase { public void test_0() throws Exception { - Assert.assertEquals("SELECT serviceTag AS \"编号\", ip AS \"IP地址\"\nFROM device", engine.explain("select *")); + Assert.assertEquals("SELECT serviceTag AS \"编号\", ip AS \"IP地址\"\nFROM device", engine.explainToMySql("select *")); Assert.assertEquals("SELECT serviceTag AS \"编号\", ip AS \"IP地址\"\nFROM device\nWHERE ip = '127.0.0.1'", - engine.explain("select 编号, IP地址 WHERE IP地址 = '127.0.0.1'")); + engine.explainToMySql("select 编号, IP地址 WHERE IP地址 = '127.0.0.1'")); } public void test_1() throws Exception { engine.setMaxLimit(10); - Assert.assertEquals("SELECT serviceTag AS \"编号\", ip AS \"IP地址\"\nFROM device\nLIMIT 10", engine.explain("select *")); + Assert.assertEquals("SELECT serviceTag AS \"编号\", ip AS \"IP地址\"\nFROM device\nLIMIT 10", engine.explainToMySql("select *")); Assert.assertEquals("SELECT serviceTag AS \"编号\", ip AS \"IP地址\"\nFROM device\nWHERE ip = '127.0.0.1'\nLIMIT 10", - engine.explain("select 编号, IP地址 WHERE IP地址 = '127.0.0.1'")); + engine.explainToMySql("select 编号, IP地址 WHERE IP地址 = '127.0.0.1'")); engine.setMaxLimit(null);