From cd6140c5bf7f44e706745a8dfb0a57a6bfcbbb4f Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 2 Feb 2013 15:53:23 +0800 Subject: [PATCH 1/3] add PagerUtils oracle support --- .../com/alibaba/druid/sql/PagerUtils.java | 107 +++++++++++++----- .../postgresql/visitor/PGOutputVisitor.java | 2 +- ...java => PagerUtilsTest_Count_MySql_0.java} | 2 +- ...java => PagerUtilsTest_Limit_mysql_0.java} | 2 +- 4 files changed, 84 insertions(+), 29 deletions(-) rename src/test/java/com/alibaba/druid/bvt/sql/{PagerUtilsTest0.java => PagerUtilsTest_Count_MySql_0.java} (97%) rename src/test/java/com/alibaba/druid/bvt/sql/{PagerUtilsTestLimit_0.java => PagerUtilsTest_Limit_mysql_0.java} (96%) diff --git a/src/main/java/com/alibaba/druid/sql/PagerUtils.java b/src/main/java/com/alibaba/druid/sql/PagerUtils.java index 4f75c76399..764543b3d4 100644 --- a/src/main/java/com/alibaba/druid/sql/PagerUtils.java +++ b/src/main/java/com/alibaba/druid/sql/PagerUtils.java @@ -17,10 +17,15 @@ package com.alibaba.druid.sql; import java.util.List; +import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; +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.SQLNumberExpr; +import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectItem; import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; @@ -54,7 +59,7 @@ public class PagerUtils { SQLSelectStatement selectStmt = (SQLSelectStatement) stmt; return count(selectStmt.getSelect(), dbType); } - + public static String limit(String sql, String dbType, int offset, int count) { List stmtList = SQLUtils.parseStatements(sql, dbType); @@ -69,58 +74,108 @@ public class PagerUtils { } SQLSelectStatement selectStmt = (SQLSelectStatement) stmt; - + return limit(selectStmt.getSelect(), dbType, offset, count); } - + public static String limit(SQLSelect select, String dbType, int offset, int count) { SQLSelectQuery query = select.getQuery(); - + + if (JdbcConstants.ORACLE.equals(dbType)) { + return limitOracle(select, dbType, offset, count); + } + if (query instanceof SQLSelectQueryBlock) { - return limit((SQLSelectQueryBlock) query, dbType, offset, count); + return limitQueryBlock(select, dbType, offset, count); } + throw new UnsupportedOperationException(); } - - public static String limit(SQLSelectQueryBlock queryBlock, String dbType, int offset, int count) { + + private static String limitQueryBlock(SQLSelect select, String dbType, int offset, int count) { + SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) select.getQuery(); if (JdbcConstants.MYSQL.equals(dbType)) { - return limitMySql((MySqlSelectQueryBlock) queryBlock, dbType, offset, count); + return limitMySqlQueryBlock((MySqlSelectQueryBlock) queryBlock, dbType, offset, count); } + throw new UnsupportedOperationException(); } - - public static String limitMySql(MySqlSelectQueryBlock queryBlock, String dbType, int offset, int count) { + + private static String limitOracle(SQLSelect select, String dbType, int offset, int count) { + SQLSelectQuery query = select.getQuery(); + + if (query instanceof SQLSelectQueryBlock) { + OracleSelectQueryBlock queryBlock = (OracleSelectQueryBlock) query; + if (select.getOrderBy() == null && offset <= 0) { + SQLExpr condition = new SQLBinaryOpExpr(new SQLIdentifierExpr("ROWNUM"), // + SQLBinaryOperator.LessThanOrEqual, // + new SQLNumberExpr(count)); + if (queryBlock.getWhere() == null) { + queryBlock.setWhere(condition); + } else { + queryBlock.setWhere(new SQLBinaryOpExpr(queryBlock.getWhere(), // + SQLBinaryOperator.BooleanAnd, // + condition)); + } + + return SQLUtils.toSQLString(select, dbType); + } + } + + OracleSelectQueryBlock countQueryBlock = new OracleSelectQueryBlock(); + countQueryBlock.getSelectList().add(new SQLSelectItem(new SQLPropertyExpr(new SQLIdentifierExpr("_X"), "*"))); + countQueryBlock.getSelectList().add(new SQLSelectItem(new SQLIdentifierExpr("ROWNUM"), "_RN")); + + countQueryBlock.setFrom(new SQLSubqueryTableSource(select, "_X")); + countQueryBlock.setWhere(new SQLBinaryOpExpr(new SQLIdentifierExpr("ROWNUM"), // + SQLBinaryOperator.LessThanOrEqual, // + new SQLNumberExpr(count + offset))); + if (offset <= 0) { + return SQLUtils.toSQLString(countQueryBlock, dbType); + } + + OracleSelectQueryBlock offsetQueryBlock = new OracleSelectQueryBlock(); + offsetQueryBlock.getSelectList().add(new SQLSelectItem(new SQLAllColumnExpr())); + offsetQueryBlock.setFrom(new SQLSubqueryTableSource(new SQLSelect(countQueryBlock), "_XX")); + offsetQueryBlock.setWhere(new SQLBinaryOpExpr(new SQLIdentifierExpr("_RN"), // + SQLBinaryOperator.GreaterThan, // + new SQLNumberExpr(offset))); + + return SQLUtils.toSQLString(offsetQueryBlock, dbType); + } + + private static String limitMySqlQueryBlock(MySqlSelectQueryBlock queryBlock, String dbType, int offset, int count) { if (queryBlock.getLimit() != null) { throw new IllegalArgumentException("limit already exists."); } - + Limit limit = new Limit(); if (offset > 0) { limit.setOffset(new SQLNumberExpr(offset)); } limit.setRowCount(new SQLNumberExpr(count)); queryBlock.setLimit(limit); - + return SQLUtils.toSQLString(queryBlock, dbType); } - + private static String count(SQLSelect select, String dbType) { if (select.getOrderBy() != null) { select.setOrderBy(null); } - + SQLSelectQuery query = select.getQuery(); clearOrderBy(query); if (query instanceof SQLSelectQueryBlock) { SQLSelectItem countItem = createCountItem(dbType); - + SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) query; - + if (queryBlock.getGroupBy() != null && queryBlock.getGroupBy().getItems().size() > 0) { - return createCountUseSubQuery(select, dbType); + return createCountUseSubQuery(select, dbType); } - + queryBlock.getSelectList().clear(); queryBlock.getSelectList().add(countItem); return SQLUtils.toSQLString(select, dbType); @@ -133,15 +188,15 @@ public class PagerUtils { private static String createCountUseSubQuery(SQLSelect select, String dbType) { SQLSelectQueryBlock countSelectQuery = createQueryBlock(dbType); - + SQLSelectItem countItem = createCountItem(dbType); countSelectQuery.getSelectList().add(countItem); - + countSelectQuery.setFrom(new SQLSubqueryTableSource(select)); - + SQLSelect countSelect = new SQLSelect(countSelectQuery); SQLSelectStatement countStmt = new SQLSelectStatement(countSelect); - + return SQLUtils.toSQLString(countStmt, dbType); } @@ -149,19 +204,19 @@ public class PagerUtils { if (JdbcConstants.MYSQL.equals(dbType)) { return new MySqlSelectQueryBlock(); } - + if (JdbcConstants.ORACLE.equals(dbType)) { return new OracleSelectQueryBlock(); } - + if (JdbcConstants.POSTGRESQL.equals(dbType)) { return new PGSelectQueryBlock(); } - + if (JdbcConstants.SQL_SERVER.equals(dbType)) { return new SQLServerSelectQueryBlock(); } - + return new SQLSelectQueryBlock(); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGOutputVisitor.java index b9bea852e3..254d97833d 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGOutputVisitor.java @@ -197,7 +197,7 @@ public class PGOutputVisitor extends SQLASTOutputVisitor implements PGASTVisitor } if (x.getGroupBy() != null) { - print(" "); + println(); x.getGroupBy().accept(this); } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest0.java b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_MySql_0.java similarity index 97% rename from src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest0.java rename to src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_MySql_0.java index d6271da251..4ca0b17534 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest0.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_MySql_0.java @@ -7,7 +7,7 @@ import org.junit.Assert; import com.alibaba.druid.sql.PagerUtils; import com.alibaba.druid.util.JdbcConstants; -public class PagerUtilsTest0 extends TestCase { +public class PagerUtilsTest_Count_MySql_0 extends TestCase { public void test_mysql_0() throws Exception { String sql = "select * from t"; diff --git a/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTestLimit_0.java b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Limit_mysql_0.java similarity index 96% rename from src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTestLimit_0.java rename to src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Limit_mysql_0.java index 38382bc614..1363ae19f6 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTestLimit_0.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Limit_mysql_0.java @@ -7,7 +7,7 @@ import org.junit.Assert; import com.alibaba.druid.sql.PagerUtils; import com.alibaba.druid.util.JdbcConstants; -public class PagerUtilsTestLimit_0 extends TestCase { +public class PagerUtilsTest_Limit_mysql_0 extends TestCase { public void test_mysql_0() throws Exception { String sql = "select * from t"; From 84f30ff2edf6aed744a7a0c7aa3bccde1d0a079f Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 2 Feb 2013 15:53:36 +0800 Subject: [PATCH 2/3] add PagerUtils oracle support --- .../sql/PagerUtilsTest_Count_Oracle_0.java | 54 ++++++++++++++ .../bvt/sql/PagerUtilsTest_Count_PG_0.java | 54 ++++++++++++++ .../sql/PagerUtilsTest_Count_SQLServer_0.java | 54 ++++++++++++++ .../sql/PagerUtilsTest_Limit_oracle_0.java | 70 +++++++++++++++++++ 4 files changed, 232 insertions(+) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_Oracle_0.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_PG_0.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_SQLServer_0.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Limit_oracle_0.java diff --git a/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_Oracle_0.java b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_Oracle_0.java new file mode 100644 index 0000000000..8d5384b978 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_Oracle_0.java @@ -0,0 +1,54 @@ +package com.alibaba.druid.bvt.sql; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.PagerUtils; +import com.alibaba.druid.util.JdbcConstants; + +public class PagerUtilsTest_Count_Oracle_0 extends TestCase { + + public void test_oracle_0() throws Exception { + String sql = "select * from t"; + String result = PagerUtils.count(sql, JdbcConstants.ORACLE); + Assert.assertEquals("SELECT COUNT(*)\n" + // + "FROM t", result); + } + + public void test_oracle_1() throws Exception { + String sql = "select id, name from t"; + String result = PagerUtils.count(sql, JdbcConstants.ORACLE); + Assert.assertEquals("SELECT COUNT(*)\n" + // + "FROM t", result); + } + + public void test_oracle_2() throws Exception { + String sql = "select id, name from t order by id"; + String result = PagerUtils.count(sql, JdbcConstants.ORACLE); + Assert.assertEquals("SELECT COUNT(*)\n" + // + "FROM t", result); + } + + public void test_oracle_group_0() throws Exception { + String sql = "select type, count(*) from t group by type"; + String result = PagerUtils.count(sql, JdbcConstants.ORACLE); + Assert.assertEquals("SELECT COUNT(*)" + // + "\nFROM (SELECT type, COUNT(*)" + // + "\n\tFROM t" + // + "\n\tGROUP BY type" + // + "\n\t)", result); + } + + public void test_oracle_union_0() throws Exception { + String sql = "select id, name from t1 union select id, name from t2 order by id"; + String result = PagerUtils.count(sql, JdbcConstants.ORACLE); + Assert.assertEquals("SELECT COUNT(*)\n" + // + "FROM (SELECT id, name" + // + "\n\tFROM t1" + // + "\n\tUNION" + // + "\n\tSELECT id, name" + // + "\n\tFROM t2" + // + "\n\t)", result); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_PG_0.java b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_PG_0.java new file mode 100644 index 0000000000..fdc02bba67 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_PG_0.java @@ -0,0 +1,54 @@ +package com.alibaba.druid.bvt.sql; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.PagerUtils; +import com.alibaba.druid.util.JdbcConstants; + +public class PagerUtilsTest_Count_PG_0 extends TestCase { + + public void test_pg_0() throws Exception { + String sql = "select * from t"; + String result = PagerUtils.count(sql, JdbcConstants.POSTGRESQL); + Assert.assertEquals("SELECT COUNT(*)\n" + // + "FROM t", result); + } + + public void test_pg_1() throws Exception { + String sql = "select id, name from t"; + String result = PagerUtils.count(sql, JdbcConstants.POSTGRESQL); + Assert.assertEquals("SELECT COUNT(*)\n" + // + "FROM t", result); + } + + public void test_pg_2() throws Exception { + String sql = "select id, name from t order by id"; + String result = PagerUtils.count(sql, JdbcConstants.POSTGRESQL); + Assert.assertEquals("SELECT COUNT(*)\n" + // + "FROM t", result); + } + + public void test_pg_group_0() throws Exception { + String sql = "select type, count(*) from t group by type"; + String result = PagerUtils.count(sql, JdbcConstants.POSTGRESQL); + Assert.assertEquals("SELECT COUNT(*)" + // + "\nFROM (SELECT type, COUNT(*)" + // + "\n\tFROM t" + // + "\n\tGROUP BY type" + // + "\n\t)", result); + } + + public void test_pg_union_0() throws Exception { + String sql = "select id, name from t1 union select id, name from t2 order by id"; + String result = PagerUtils.count(sql, JdbcConstants.POSTGRESQL); + Assert.assertEquals("SELECT COUNT(*)\n" + // + "FROM (SELECT id, name" + // + "\n\tFROM t1" + // + "\n\tUNION" + // + "\n\tSELECT id, name" + // + "\n\tFROM t2" + // + "\n\t)", result); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_SQLServer_0.java b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_SQLServer_0.java new file mode 100644 index 0000000000..2d64e340b2 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_SQLServer_0.java @@ -0,0 +1,54 @@ +package com.alibaba.druid.bvt.sql; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.PagerUtils; +import com.alibaba.druid.util.JdbcConstants; + +public class PagerUtilsTest_Count_SQLServer_0 extends TestCase { + + public void test_sqlserver_0() throws Exception { + String sql = "select * from t"; + String result = PagerUtils.count(sql, JdbcConstants.SQL_SERVER); + Assert.assertEquals("SELECT COUNT(*)\n" + // + "FROM t", result); + } + + public void test_sqlserver_1() throws Exception { + String sql = "select id, name from t"; + String result = PagerUtils.count(sql, JdbcConstants.SQL_SERVER); + Assert.assertEquals("SELECT COUNT(*)\n" + // + "FROM t", result); + } + + public void test_sqlserver_2() throws Exception { + String sql = "select id, name from t order by id"; + String result = PagerUtils.count(sql, JdbcConstants.SQL_SERVER); + Assert.assertEquals("SELECT COUNT(*)\n" + // + "FROM t", result); + } + + public void test_sqlserver_group_0() throws Exception { + String sql = "select type, count(*) from t group by type"; + String result = PagerUtils.count(sql, JdbcConstants.SQL_SERVER); + Assert.assertEquals("SELECT COUNT(*)" + // + "\nFROM (SELECT type, COUNT(*)" + // + "\n\tFROM t" + // + "\n\tGROUP BY type" + // + "\n\t)", result); + } + + public void test_sqlserver_union_0() throws Exception { + String sql = "select id, name from t1 union select id, name from t2 order by id"; + String result = PagerUtils.count(sql, JdbcConstants.SQL_SERVER); + Assert.assertEquals("SELECT COUNT(*)\n" + // + "FROM (SELECT id, name" + // + "\n\tFROM t1" + // + "\n\tUNION" + // + "\n\tSELECT id, name" + // + "\n\tFROM t2" + // + "\n\t)", result); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Limit_oracle_0.java b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Limit_oracle_0.java new file mode 100644 index 0000000000..6481dca199 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Limit_oracle_0.java @@ -0,0 +1,70 @@ +package com.alibaba.druid.bvt.sql; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.PagerUtils; +import com.alibaba.druid.util.JdbcConstants; + +public class PagerUtilsTest_Limit_oracle_0 extends TestCase { + + public void test_oracle_oderby_0() throws Exception { + String sql = "select * from t order by id"; + String result = PagerUtils.limit(sql, JdbcConstants.ORACLE, 0, 10); + Assert.assertEquals("SELECT _X.*, ROWNUM AS _RN" + // + "\nFROM (SELECT *" + // + "\n\tFROM t" + // + "\n\tORDER BY id" + // + "\n\t) _X" + // + "\nWHERE ROWNUM <= 10", result); + } + + public void test_oracle_0() throws Exception { + String sql = "select * from t"; + String result = PagerUtils.limit(sql, JdbcConstants.ORACLE, 0, 10); + Assert.assertEquals("SELECT *" + // + "\nFROM t" + // + "\nWHERE ROWNUM <= 10", result); + } + + public void test_oracle_1() throws Exception { + String sql = "select * from t"; + String result = PagerUtils.limit(sql, JdbcConstants.ORACLE, 10, 10); + Assert.assertEquals("SELECT *" + // + "\nFROM (SELECT _X.*, ROWNUM AS _RN" + // + "\n\tFROM (SELECT *" + // + "\n\t\tFROM t" + // + "\n\t\t) _X" + // + "\n\tWHERE ROWNUM <= 20" + // + "\n\t) _XX" + // + "\nWHERE _RN > 10", result); + } + + public void test_oracle_2() throws Exception { + String sql = "select * from t"; + String result = PagerUtils.limit(sql, JdbcConstants.ORACLE, 20, 10); + Assert.assertEquals("SELECT *" + // + "\nFROM (SELECT _X.*, ROWNUM AS _RN" + // + "\n\tFROM (SELECT *" + // + "\n\t\tFROM t" + // + "\n\t\t) _X" + // + "\n\tWHERE ROWNUM <= 30" + // + "\n\t) _XX" + // + "\nWHERE _RN > 20", result); + } + + public void test_oracle_3() throws Exception { + String sql = "select id, name, salary from t order by id, name"; + String result = PagerUtils.limit(sql, JdbcConstants.ORACLE, 20, 10); + Assert.assertEquals("SELECT *" + // + "\nFROM (SELECT _X.*, ROWNUM AS _RN" + // + "\n\tFROM (SELECT id, name, salary" + // + "\n\t\tFROM t" + // + "\n\t\tORDER BY id, name" + // + "\n\t\t) _X" + // + "\n\tWHERE ROWNUM <= 30" + // + "\n\t) _XX" + // + "\nWHERE _RN > 20", result); + } +} From c0c28920240d9f803514d3c00d296a0116bf20cd Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 2 Feb 2013 20:33:57 +0800 Subject: [PATCH 3/3] bug fixed for sql parser --- .../alibaba/druid/sql/visitor/SQLASTOutputVisitor.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java index 09cd66d802..f1fc43ce06 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -1292,12 +1292,14 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter { @Override public boolean visit(SQLOver x) { print("OVER ("); - printAndAccept(x.getPartitionBy(), ", "); - print(")"); + if (x.getPartitionBy().size() > 0) { + printAndAccept(x.getPartitionBy(), ", "); + print(' '); + } if (x.getOrderBy() != null) { - print(" "); x.getOrderBy().accept(this); } + print(")"); return false; }