Merge pull request #719 from wenshao/master

improved sql parser, support translate oracle to mysql
This commit is contained in:
高铁
2014-11-19 10:25:06 +08:00
10 changed files with 272 additions and 8 deletions
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}