diff --git a/src/main/java/com/alibaba/druid/sql/SQLUtils.java b/src/main/java/com/alibaba/druid/sql/SQLUtils.java index 2328dca53c..a11fef46f2 100644 --- a/src/main/java/com/alibaba/druid/sql/SQLUtils.java +++ b/src/main/java/com/alibaba/druid/sql/SQLUtils.java @@ -30,6 +30,7 @@ import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor; import com.alibaba.druid.sql.dialect.odps.visitor.OdpsOutputVisitor; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleOutputVisitor; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleSchemaStatVisitor; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleToMySqlOutputVisitor; import com.alibaba.druid.sql.dialect.postgresql.visitor.PGOutputVisitor; import com.alibaba.druid.sql.dialect.postgresql.visitor.PGSchemaStatVisitor; import com.alibaba.druid.sql.dialect.sqlserver.visitor.SQLServerOutputVisitor; @@ -69,7 +70,7 @@ public class SQLUtils { if (JdbcUtils.DB2.equals(dbType)) { return toDB2String(sqlObject); } - + if (JdbcUtils.ODPS.equals(dbType)) { return toDB2String(sqlObject); } @@ -92,11 +93,11 @@ public class SQLUtils { String sql = out.toString(); return sql; } - + public static String toMySqlString(SQLObject sqlObject) { StringBuilder out = new StringBuilder(); sqlObject.accept(new MySqlOutputVisitor(out)); - + String sql = out.toString(); return sql; } @@ -112,7 +113,7 @@ public class SQLUtils { public static String formatOracle(String sql) { return format(sql, JdbcUtils.ORACLE); } - + public static String formatOdps(String sql) { return format(sql, JdbcUtils.ODPS); } @@ -120,7 +121,7 @@ public class SQLUtils { public static String formatSQLServer(String sql) { return format(sql, JdbcUtils.SQL_SERVER); } - + public static String toOracleString(SQLObject sqlObject) { StringBuilder out = new StringBuilder(); sqlObject.accept(new OracleOutputVisitor(out, false)); @@ -202,9 +203,9 @@ public class SQLUtils { if (parameters != null) { visitor.setParameters(parameters); } - - for(int i =0; i0) { + + for (int i = 0; i < statementList.size(); i++) { + if (i > 0) { out.append(";\n"); } statementList.get(i).accept(visitor); @@ -332,4 +333,18 @@ public class SQLUtils { } return groupList; } + + public static String translateOracleToMySql(String sql) { + List stmtList = toStatementList(sql, JdbcConstants.ORACLE); + + StringBuilder out = new StringBuilder(); + OracleToMySqlOutputVisitor visitor = new OracleToMySqlOutputVisitor(out, false); + for (int i = 0; i < stmtList.size(); ++i) { + stmtList.get(i).accept(visitor); + } + + String mysqlSql = out.toString(); + return mysqlSql; + + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelect.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelect.java index 58371d0a0c..37330d8762 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelect.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelect.java @@ -15,6 +15,7 @@ */ package com.alibaba.druid.sql.dialect.oracle.ast.stmt; +import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; @@ -67,4 +68,8 @@ public class OracleSelect extends SQLSelect { } visitor.endVisit(this); } + + public String toString() { + return SQLUtils.toOracleString(this); + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectJoin.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectJoin.java index 04ff92a4e5..137ad89733 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectJoin.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectJoin.java @@ -15,6 +15,7 @@ */ package com.alibaba.druid.sql.dialect.oracle.ast.stmt; +import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause; @@ -90,4 +91,7 @@ public class OracleSelectJoin extends SQLJoinTableSource implements OracleSelect } } + public String toString () { + return SQLUtils.toOracleString(this); + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java index 01fb203cd6..ca8cde93fb 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java @@ -18,6 +18,7 @@ package com.alibaba.druid.sql.dialect.oracle.ast.stmt; import java.util.ArrayList; import java.util.List; +import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.SQLCommentHint; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause; @@ -78,4 +79,8 @@ public class OracleSelectQueryBlock extends SQLSelectQueryBlock { } visitor.endVisit(this); } + + public String toString() { + return SQLUtils.toOracleString(this); + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectSubqueryTableSource.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectSubqueryTableSource.java index 1146d7bd5a..1a19e6a336 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectSubqueryTableSource.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectSubqueryTableSource.java @@ -15,6 +15,7 @@ */ package com.alibaba.druid.sql.dialect.oracle.ast.stmt; +import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause; @@ -73,4 +74,7 @@ public class OracleSelectSubqueryTableSource extends SQLSubqueryTableSource impl visitor.endVisit(this); } + public String toString () { + return SQLUtils.toOracleString(this); + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableReference.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableReference.java index 17687cd5d6..88f9d9d295 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableReference.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableReference.java @@ -15,6 +15,7 @@ */ package com.alibaba.druid.sql.dialect.oracle.ast.stmt; +import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.PartitionExtensionClause; @@ -109,4 +110,8 @@ public class OracleSelectTableReference extends SQLExprTableSource implements Or buf.append(this.alias); } } + + public String toString () { + return SQLUtils.toOracleString(this); + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleToMySqlOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleToMySqlOutputVisitor.java new file mode 100644 index 0000000000..0242e292a5 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleToMySqlOutputVisitor.java @@ -0,0 +1,145 @@ +package com.alibaba.druid.sql.dialect.oracle.visitor; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.ast.statement.SQLSelectItem; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +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; + +public class OracleToMySqlOutputVisitor extends OracleOutputVisitor { + + public OracleToMySqlOutputVisitor(Appendable appender, boolean printPostSemi){ + super(appender, printPostSemi); + } + + public OracleToMySqlOutputVisitor(Appendable appender){ + super(appender); + } + + public boolean visit(OracleSelectQueryBlock x) { + boolean parentIsSelectStatment = false; + { + if (x.getParent() instanceof SQLSelect) { + SQLSelect select = (SQLSelect) x.getParent(); + if (select.getParent() instanceof SQLSelectStatement) { + parentIsSelectStatment = true; + } + } + } + + if (!parentIsSelectStatment) { + return super.visit(x); + } + + if (x.getWhere() instanceof SQLBinaryOpExpr // + && x.getFrom() instanceof SQLSubqueryTableSource // + ) { + int rownum; + String ident; + SQLBinaryOpExpr where = (SQLBinaryOpExpr) x.getWhere(); + if (where.getRight() instanceof SQLIntegerExpr && where.getLeft() instanceof SQLIdentifierExpr) { + rownum = ((SQLIntegerExpr) where.getRight()).getNumber().intValue(); + ident = ((SQLIdentifierExpr) where.getLeft()).getName(); + } else { + return super.visit(x); + } + + SQLSelect select = ((SQLSubqueryTableSource) x.getFrom()).getSelect(); + SQLSelectQueryBlock queryBlock = null; + SQLSelect subSelect = null; + SQLBinaryOpExpr subWhere = null; + boolean isSubQueryRowNumMapping = false; + + if (select.getQuery() instanceof SQLSelectQueryBlock) { + queryBlock = (SQLSelectQueryBlock) select.getQuery(); + if (queryBlock.getWhere() instanceof SQLBinaryOpExpr) { + subWhere = (SQLBinaryOpExpr) queryBlock.getWhere(); + } + + for (SQLSelectItem selectItem : queryBlock.getSelectList()) { + if (isRowNumber(selectItem.getExpr())) { + if (where.getLeft() instanceof SQLIdentifierExpr + && ((SQLIdentifierExpr) where.getLeft()).getName().equals(selectItem.getAlias())) { + isSubQueryRowNumMapping = true; + } + } + } + + SQLTableSource subTableSource = queryBlock.getFrom(); + if (subTableSource instanceof SQLSubqueryTableSource) { + subSelect = ((SQLSubqueryTableSource) subTableSource).getSelect(); + } + } + + if ("ROWNUM".equalsIgnoreCase(ident)) { + SQLBinaryOperator op = where.getOperator(); + Integer limit = null; + if (op == SQLBinaryOperator.LessThanOrEqual) { + limit = rownum; + } else if (op == SQLBinaryOperator.LessThan) { + limit = rownum - 1; + } + + if (limit != null) { + select.accept(this); + println(); + print("LIMIT "); + print(limit); + return false; + } + } else if (isSubQueryRowNumMapping) { + SQLBinaryOperator op = where.getOperator(); + SQLBinaryOperator subOp = subWhere.getOperator(); + + if (isRowNumber(subWhere.getLeft()) // + && subWhere.getRight() instanceof SQLIntegerExpr) { + + int subRownum = ((SQLIntegerExpr) subWhere.getRight()).getNumber().intValue(); + + Integer offset = null; + if (op == SQLBinaryOperator.GreaterThanOrEqual) { + offset = rownum + 1; + } else if (op == SQLBinaryOperator.GreaterThan) { + offset = rownum; + } + + if (offset != null) { + Integer limit = null; + if (subOp == SQLBinaryOperator.LessThanOrEqual) { + limit = subRownum - offset; + } else if (subOp == SQLBinaryOperator.LessThan) { + limit = subRownum - 1 - offset; + } + + if (limit != null) { + subSelect.accept(this); + println(); + print("LIMIT "); + print(offset); + print(", "); + print(limit); + return false; + } + } + } + } + } + return super.visit(x); + } + + static boolean isRowNumber(SQLExpr expr) { + if (expr instanceof SQLIdentifierExpr) { + String lownerName = ((SQLIdentifierExpr) expr).getLowerName(); + return "rownum".equals(lownerName); + } + + return false; + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/tomysql/OracleToMySql_PageTest.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/tomysql/OracleToMySql_PageTest.java new file mode 100644 index 0000000000..1048d46ac3 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/tomysql/OracleToMySql_PageTest.java @@ -0,0 +1,26 @@ +package com.alibaba.druid.bvt.sql.oracle.tomysql; + +import org.junit.Assert; + +import junit.framework.TestCase; + +import com.alibaba.druid.sql.SQLUtils; + +public class OracleToMySql_PageTest extends TestCase { + + public void test_page() throws Exception { + String sql = "SELECT XX.*, ROWNUM AS RN" + // + "\nFROM (SELECT *" + // + "\n\tFROM t" + // + "\n\tORDER BY id" + // + "\n\t) XX" + // + "\nWHERE ROWNUM <= 10"; + + String mysqlSql = SQLUtils.translateOracleToMySql(sql); + Assert.assertEquals("SELECT *"// + + "\nFROM t"// + + "\nORDER BY id"// + + "\nLIMIT 10", mysqlSql); + System.out.println(mysqlSql); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/tomysql/OracleToMySql_PageTest1.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/tomysql/OracleToMySql_PageTest1.java new file mode 100644 index 0000000000..1d211e4dc1 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/tomysql/OracleToMySql_PageTest1.java @@ -0,0 +1,29 @@ +package com.alibaba.druid.bvt.sql.oracle.tomysql; + +import org.junit.Assert; + +import junit.framework.TestCase; + +import com.alibaba.druid.sql.SQLUtils; + +public class OracleToMySql_PageTest1 extends TestCase { + + public void test_page() throws Exception { + String sql = "SELECT *" + // + "\nFROM (SELECT XX.*, ROWNUM AS RN" + // + "\n\tFROM (SELECT *" + // + "\n\t\tFROM t" + // + "\n\t\tORDER BY id" + // + "\n\t\t) XX" + // + "\n\tWHERE ROWNUM <= 20" + // + "\n\t) XXX" + // + "\nWHERE RN > 10"; + + String mysqlSql = SQLUtils.translateOracleToMySql(sql); + Assert.assertEquals("SELECT *"// + + "\nFROM t"// + + "\nORDER BY id"// + + "\nLIMIT 10, 10", mysqlSql); + System.out.println(mysqlSql); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/tomysql/OracleToMySql_PageTest2.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/tomysql/OracleToMySql_PageTest2.java new file mode 100644 index 0000000000..e6c2817c12 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/tomysql/OracleToMySql_PageTest2.java @@ -0,0 +1,26 @@ +package com.alibaba.druid.bvt.sql.oracle.tomysql; + +import org.junit.Assert; + +import junit.framework.TestCase; + +import com.alibaba.druid.sql.SQLUtils; + +public class OracleToMySql_PageTest2 extends TestCase { + + public void test_page() throws Exception { + String sql = "SELECT XX.*, ROWNUM AS RN" + // + "\nFROM (SELECT *" + // + "\n\tFROM t" + // + "\n\tORDER BY id" + // + "\n\t) XX" + // + "\nWHERE ROWNUM < 10"; + + String mysqlSql = SQLUtils.translateOracleToMySql(sql); + Assert.assertEquals("SELECT *"// + + "\nFROM t"// + + "\nORDER BY id"// + + "\nLIMIT 9", mysqlSql); + System.out.println(mysqlSql); + } +}