mirror of
https://github.com/alibaba/druid.git
synced 2023-03-04 19:17:16 +00:00
Merge pull request #719 from wenshao/master
improved sql parser, support translate oracle to mysql
This commit is contained in:
@@ -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; i<statementList.size(); i++) {
|
||||
if(i>0) {
|
||||
|
||||
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<SQLStatement> 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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+145
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user