From a288d1014bf49f5867a9ed579e72db60a66cba9d Mon Sep 17 00:00:00 2001 From: wenshao Date: Wed, 12 Jul 2017 01:21:47 +0800 Subject: [PATCH] bug fixed for sql server parser. for issue #1795 --- .../com/alibaba/druid/sql/PagerUtils.java | 15 +- .../parser/SQLServerSelectParser.java | 2 + .../visitor/SQLServerOutputVisitor.java | 12 +- .../druid/sql/parser/SQLSelectParser.java | 3 +- .../sql/visitor/SQLASTOutputVisitor.java | 30 ++-- .../sql/PagerUtilsTest_Count_SQLServer_0.java | 10 +- .../sql/sqlserver/SQLServerSelectTest24.java | 151 ++++++++++++++++++ 7 files changed, 195 insertions(+), 28 deletions(-) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest24.java diff --git a/src/main/java/com/alibaba/druid/sql/PagerUtils.java b/src/main/java/com/alibaba/druid/sql/PagerUtils.java index c651acb5fc..f8a385dd1f 100644 --- a/src/main/java/com/alibaba/druid/sql/PagerUtils.java +++ b/src/main/java/com/alibaba/druid/sql/PagerUtils.java @@ -404,7 +404,8 @@ public class PagerUtils { SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) query; - if (queryBlock.getGroupBy() != null && queryBlock.getGroupBy().getItems().size() > 0) { + if (queryBlock.getGroupBy() != null + && queryBlock.getGroupBy().getItems().size() > 0) { return createCountUseSubQuery(select, dbType); } @@ -489,16 +490,8 @@ public class PagerUtils { private static void clearOrderBy(SQLSelectQuery query) { if (query instanceof SQLSelectQueryBlock) { SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) query; - if (queryBlock instanceof MySqlSelectQueryBlock) { - MySqlSelectQueryBlock mysqlQueryBlock = (MySqlSelectQueryBlock) queryBlock; - if (mysqlQueryBlock.getOrderBy() != null) { - mysqlQueryBlock.setOrderBy(null); - } - } else if (queryBlock instanceof PGSelectQueryBlock) { - PGSelectQueryBlock pgQueryBlock = (PGSelectQueryBlock) queryBlock; - if (pgQueryBlock.getOrderBy() != null) { - pgQueryBlock.setOrderBy(null); - } + if (queryBlock.getOrderBy() != null) { + queryBlock.setOrderBy(null); } return; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerSelectParser.java index 3ec8c6ba4b..3e857e57a9 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerSelectParser.java @@ -163,6 +163,8 @@ public class SQLServerSelectParser extends SQLSelectParser { parseGroupBy(queryBlock); + queryBlock.setOrderBy(this.exprParser.parseOrderBy()); + parseFetchClause(queryBlock); return queryRest(queryBlock); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerOutputVisitor.java index 4829d330a3..1403361bd7 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerOutputVisitor.java @@ -16,6 +16,7 @@ package com.alibaba.druid.sql.dialect.sqlserver.visitor; import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.SQLSetQuantifier; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.statement.*; @@ -82,9 +83,16 @@ public class SQLServerOutputVisitor extends SQLASTOutputVisitor implements SQLSe x.getWhere().accept(this); } - if (x.getGroupBy() != null) { + SQLSelectGroupByClause groupBy = x.getGroupBy(); + if (groupBy != null) { println(); - x.getGroupBy().accept(this); + groupBy.accept(this); + } + + SQLOrderBy orderBy = x.getOrderBy(); + if (orderBy != null) { + println(); + orderBy.accept(this); } printFetchFirst(x); diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java index 467c350d0f..61d50b8b4c 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java @@ -620,7 +620,8 @@ public class SQLSelectParser extends SQLParser { if (lexer.token() == Token.FETCH) { lexer.nextToken(); - if (lexer.token() == Token.FIRST) { + if (lexer.token() == Token.FIRST + || identifierEquals("NEXT")) { lexer.nextToken(); } else { acceptIdentifier("FIRST"); 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 1be67a4615..e2e97bdeeb 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -1370,14 +1370,16 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet printHierarchical(x); - if (x.getGroupBy() != null) { + SQLSelectGroupByClause groupBy = x.getGroupBy(); + if (groupBy != null) { println(); - x.getGroupBy().accept(this); + groupBy.accept(this); } - - if (x.getOrderBy() != null) { + + SQLOrderBy orderBy = x.getOrderBy(); + if (orderBy != null) { println(); - x.getOrderBy().accept(this); + orderBy.accept(this); } if (!JdbcConstants.INFORMIX.equals(dbType)) { @@ -1430,11 +1432,21 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet if (offset != null) { print0(ucase ? "OFFSET " : "offset "); offset.accept(this); - print0(ucase ? " ROWS " : " rows "); + print0(ucase ? " ROWS" : " rows"); + } + + if (first != null) { + if (offset != null) { + print(' '); + } + if (JdbcConstants.SQL_SERVER.equals(dbType) && offset != null) { + print0(ucase ? "FETCH NEXT " : "fetch next "); + } else { + print0(ucase ? "FETCH FIRST " : "fetch first "); + } + first.accept(this); + print0(ucase ? " ROWS ONLY" : " rows only"); } - print0(ucase ? "FETCH FIRST " : "fetch first "); - first.accept(this); - print0(ucase ? " ROWS ONLY" : " rows only"); } else { println(); limit.accept(this); 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 index 8f7fa189e0..7465979e92 100644 --- 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 @@ -12,28 +12,28 @@ 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" + // + 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" + // + 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" + // + 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(*)\n" + + assertEquals("SELECT COUNT(*)\n" + "FROM (\n" + "\tSELECT type, COUNT(*)\n" + "\tFROM t\n" + @@ -44,7 +44,7 @@ public class PagerUtilsTest_Count_SQLServer_0 extends TestCase { 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" + + assertEquals("SELECT COUNT(*)\n" + "FROM (\n" + "\tSELECT id, name\n" + "\tFROM t1\n" + diff --git a/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest24.java b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest24.java new file mode 100644 index 0000000000..998954f0f8 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest24.java @@ -0,0 +1,151 @@ +/* + * Copyright 1999-2017 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.sqlserver; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser; +import junit.framework.TestCase; +import org.junit.Assert; + +public class SQLServerSelectTest24 extends TestCase { + + public void test_simple() throws Exception { + String sql = // + "SELECT TOP 100 *\n" + + "FROM\n" + + "(SELECT ROW_NUMBER()\n" + + "OVER (ORDER BY DateUpdated DESC) PAGE_ROW_NUMBER, *\n" + + "FROM\n" + + "(SELECT x.*\n" + + "FROM\n" + + "(SELECT TOP 2000 a.*\n" + + "FROM pk_Battle a\n" + + "WHERE a.RequestUserId = 33460323\n" + + "AND a.IsActive = 1\n" + + "AND a.BattleType <> 2\n" + + "AND a.Status NOT IN (0, 2)\n" + + "AND EXISTS\n" + + "(SELECT TOP 1 1\n" + + "FROM pk_BattleExt\n" + + "WHERE BattleId = a.Id\n" + + "AND RequestIsViewResults = 0)\n" + + "ORDER BY a.Id DESC\n" + + "UNION\n" + + "SELECT TOP 1000 a.*\n" + + "FROM pk_Battle a\n" + + "WHERE a.ResponseUserId = 33460323\n" + + "AND a.IsActive = 1\n" + + "AND a.BattleType = 1\n" + + "AND a.Status NOT IN (0, 2)\n" + + "AND EXISTS\n" + + "(SELECT TOP 1 1\n" + + "FROM pk_BattleExt\n" + + "WHERE BattleId = a.Id\n" + + "AND ResponseIsViewResults = 0)\n" + + "ORDER BY a.Id DESC) x) AS PAGE_TABLE_ALIAS) AS PAGE_TABLE_ALIAS\n" + + "WHERE PAGE_ROW_NUMBER > 0\n" + + "ORDER BY PAGE_ROW_NUMBER"; // + + SQLServerStatementParser parser = new SQLServerStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + + { + String text = SQLUtils.toSQLServerString(stmt); + + Assert.assertEquals("SELECT TOP 100 *\n" + + "FROM (\n" + + "\tSELECT ROW_NUMBER() OVER (ORDER BY DateUpdated DESC) AS PAGE_ROW_NUMBER, *\n" + + "\tFROM (\n" + + "\t\tSELECT x.*\n" + + "\t\tFROM (\n" + + "\t\t\tSELECT TOP 2000 a.*\n" + + "\t\t\tFROM pk_Battle a\n" + + "\t\t\tWHERE a.RequestUserId = 33460323\n" + + "\t\t\t\tAND a.IsActive = 1\n" + + "\t\t\t\tAND a.BattleType <> 2\n" + + "\t\t\t\tAND a.Status NOT IN (0, 2)\n" + + "\t\t\t\tAND EXISTS (\n" + + "\t\t\t\t\tSELECT TOP 1 1\n" + + "\t\t\t\t\tFROM pk_BattleExt\n" + + "\t\t\t\t\tWHERE BattleId = a.Id\n" + + "\t\t\t\t\t\tAND RequestIsViewResults = 0\n" + + "\t\t\t\t)\n" + + "\t\t\tORDER BY a.Id DESC\n" + + "\t\t\tUNION\n" + + "\t\t\tSELECT TOP 1000 a.*\n" + + "\t\t\tFROM pk_Battle a\n" + + "\t\t\tWHERE a.ResponseUserId = 33460323\n" + + "\t\t\t\tAND a.IsActive = 1\n" + + "\t\t\t\tAND a.BattleType = 1\n" + + "\t\t\t\tAND a.Status NOT IN (0, 2)\n" + + "\t\t\t\tAND EXISTS (\n" + + "\t\t\t\t\tSELECT TOP 1 1\n" + + "\t\t\t\t\tFROM pk_BattleExt\n" + + "\t\t\t\t\tWHERE BattleId = a.Id\n" + + "\t\t\t\t\t\tAND ResponseIsViewResults = 0\n" + + "\t\t\t\t)\n" + + "\t\t\tORDER BY a.Id DESC\n" + + "\t\t) x\n" + + "\t) PAGE_TABLE_ALIAS\n" + + ") PAGE_TABLE_ALIAS\n" + + "WHERE PAGE_ROW_NUMBER > 0\n" + + "ORDER BY PAGE_ROW_NUMBER", text); + } + { + String text = SQLUtils.toSQLServerString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION); + + Assert.assertEquals("select top 100 *\n" + + "from (\n" + + "\tselect row_number() over (order by DateUpdated desc) as PAGE_ROW_NUMBER, *\n" + + "\tfrom (\n" + + "\t\tselect x.*\n" + + "\t\tfrom (\n" + + "\t\t\tselect top 2000 a.*\n" + + "\t\t\tfrom pk_Battle a\n" + + "\t\t\twhere a.RequestUserId = 33460323\n" + + "\t\t\t\tand a.IsActive = 1\n" + + "\t\t\t\tand a.BattleType <> 2\n" + + "\t\t\t\tand a.Status not in (0, 2)\n" + + "\t\t\t\tand exists (\n" + + "\t\t\t\t\tselect top 1 1\n" + + "\t\t\t\t\tfrom pk_BattleExt\n" + + "\t\t\t\t\twhere BattleId = a.Id\n" + + "\t\t\t\t\t\tand RequestIsViewResults = 0\n" + + "\t\t\t\t)\n" + + "\t\t\torder by a.Id desc\n" + + "\t\t\tunion\n" + + "\t\t\tselect top 1000 a.*\n" + + "\t\t\tfrom pk_Battle a\n" + + "\t\t\twhere a.ResponseUserId = 33460323\n" + + "\t\t\t\tand a.IsActive = 1\n" + + "\t\t\t\tand a.BattleType = 1\n" + + "\t\t\t\tand a.Status not in (0, 2)\n" + + "\t\t\t\tand exists (\n" + + "\t\t\t\t\tselect top 1 1\n" + + "\t\t\t\t\tfrom pk_BattleExt\n" + + "\t\t\t\t\twhere BattleId = a.Id\n" + + "\t\t\t\t\t\tand ResponseIsViewResults = 0\n" + + "\t\t\t\t)\n" + + "\t\t\torder by a.Id desc\n" + + "\t\t) x\n" + + "\t) PAGE_TABLE_ALIAS\n" + + ") PAGE_TABLE_ALIAS\n" + + "where PAGE_ROW_NUMBER > 0\n" + + "order by PAGE_ROW_NUMBER", text); + } + } +}