From 9ad2d6ad13c2dbe4a9de3f3f3955456489a36778 Mon Sep 17 00:00:00 2001 From: wenshao Date: Mon, 7 Jan 2013 07:46:41 +0800 Subject: [PATCH] improve sql parser, sql server support with --- .../sqlserver/parser/SQLServerLexer.java | 94 +++++++++++++++ .../parser/SQLServerSelectParser.java | 2 + .../parser/SQLServerStatementParser.java | 12 ++ .../druid/sql/parser/SQLStatementParser.java | 7 +- .../sql/sqlserver/SQLServerSelectTest6.java | 62 ++++++++++ .../sql/sqlserver/SQLServerSelectTest7.java | 109 ++++++++++++++++++ 6 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest6.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest7.java diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerLexer.java index 1d03d19c36..4cc3ad1a8a 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerLexer.java @@ -15,11 +15,14 @@ */ package com.alibaba.druid.sql.dialect.sqlserver.parser; +import static com.alibaba.druid.sql.parser.LayoutCharacters.EOI; + import java.util.HashMap; import java.util.Map; import com.alibaba.druid.sql.parser.Keywords; import com.alibaba.druid.sql.parser.Lexer; +import com.alibaba.druid.sql.parser.NotAllowCommentException; import com.alibaba.druid.sql.parser.Token; public class SQLServerLexer extends Lexer { @@ -48,4 +51,95 @@ public class SQLServerLexer extends Lexer { super(input); super.keywods = DEFAULT_SQL_SERVER_KEYWORDS; } + + public void scanComment() { + if (ch != '/' && ch != '-') { + throw new IllegalStateException(); + } + + mark = pos; + bufPos = 0; + scanChar(); + + // /*+ */ + if (ch == '*') { + scanChar(); + bufPos++; + + while (ch == ' ') { + scanChar(); + bufPos++; + } + + boolean isHint = false; + int startHintSp = bufPos + 1; + if (ch == '!') { + isHint = true; + scanChar(); + bufPos++; + } + + for (;;) { + if (ch == '*' && charAt(pos + 1) == '/') { + bufPos += 2; + scanChar(); + scanChar(); + break; + } + + scanChar(); + bufPos++; + } + + if (isHint) { + stringVal = subString(mark + startHintSp, (bufPos - startHintSp) - 1); + token = Token.HINT; + } else { + stringVal = subString(mark, bufPos); + token = Token.MULTI_LINE_COMMENT; + } + + if (token != Token.HINT && !isAllowComment()) { + throw new NotAllowCommentException(); + } + + return; + } + + if (!isAllowComment()) { + throw new NotAllowCommentException(); + } + + if (ch == '/' || ch == '-') { + scanChar(); + bufPos++; + + for (;;) { + if (ch == '\r') { + if (charAt(pos + 1) == '\n') { + bufPos += 2; + scanChar(); + break; + } + bufPos++; + break; + } else if (ch == EOI) { + break; + } + + if (ch == '\n') { + scanChar(); + bufPos++; + break; + } + + scanChar(); + bufPos++; + } + + stringVal = subString(mark + 1, bufPos); + token = Token.LINE_COMMENT; + 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 6fae0bb0ad..19908b74e4 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 @@ -37,6 +37,8 @@ public class SQLServerSelectParser extends SQLSelectParser { public SQLSelect select() { SQLServerSelect select = new SQLServerSelect(); + + withSubquery(select); select.setQuery(query()); select.setOrderBy(parseOrderBy()); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java index b29aa7d642..6b196127ec 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java @@ -15,6 +15,8 @@ */ package com.alibaba.druid.sql.dialect.sqlserver.parser; +import java.util.List; + import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; @@ -40,6 +42,16 @@ public class SQLServerStatementParser extends SQLStatementParser { super(new SQLServerExprParser(lexer)); } + public boolean parseStatementListDialect(List statementList) { + if (lexer.token() == Token.WITH) { + SQLStatement stmt = parseSelect(); + statementList.add(stmt); + return true; + } + + return false; + } + public SQLStatement parseInsert() { SQLServerInsertStatement insertStatement = new SQLServerInsertStatement(); diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java index 4837ed096d..a41e0ebd8b 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -38,6 +38,7 @@ import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; import com.alibaba.druid.sql.ast.statement.SQLReleaseSavePointStatement; import com.alibaba.druid.sql.ast.statement.SQLRollbackStatement; import com.alibaba.druid.sql.ast.statement.SQLSavePointStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.ast.statement.SQLSetStatement; import com.alibaba.druid.sql.ast.statement.SQLTableSource; @@ -80,7 +81,7 @@ public class SQLStatementParser extends SQLParser { continue; } - if (lexer.token() == (Token.SELECT)) { + if (lexer.token() == Token.SELECT) { statementList.add(parseSelect()); continue; } @@ -545,7 +546,9 @@ public class SQLStatementParser extends SQLParser { } public SQLSelectStatement parseSelect() { - return new SQLSelectStatement(createSQLSelectParser().select()); + SQLSelectParser selectParser = createSQLSelectParser(); + SQLSelect select = selectParser.select(); + return new SQLSelectStatement(select); } public SQLSelectParser createSQLSelectParser() { diff --git a/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest6.java b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest6.java new file mode 100644 index 0000000000..270091be86 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest6.java @@ -0,0 +1,62 @@ +/* + * Copyright 1999-2011 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 junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser; +import com.alibaba.druid.sql.test.TestUtils; + +public class SQLServerSelectTest6 extends TestCase { + + public void test_isEmpty() throws Exception { + String sql = "WITH DirReps(ManagerID, DirectReports) AS " + // + "(" + // + " SELECT ManagerID, COUNT(*) " + // + " FROM HumanResources.Employee AS e" + // + " WHERE ManagerID IS NOT NULL" + // + " GROUP BY ManagerID" + // + ")" + // + "SELECT ManagerID, DirectReports " + // + "FROM DirReps " + // + "ORDER BY ManagerID;"; + + String expect = "WITH" + + "\n\tDirReps (ManagerID, DirectReports)" + + "\n\tAS" + + "\n\t(" + + "\n\t\tSELECT ManagerID, COUNT(*)" + + "\n\t\tFROM HumanResources.Employee e" + + "\n\t\tWHERE ManagerID IS NOT NULL" + + "\n\t\tGROUP BY ManagerID" + + "\n\t)" + + "\nSELECT ManagerID, DirectReports" + + "\nFROM DirReps" + + "\nORDER BY ManagerID"; + + SQLServerStatementParser parser = new SQLServerStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + + String text = TestUtils.outputSqlServer(stmt); + + Assert.assertEquals(expect, text); + + System.out.println(text); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest7.java b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest7.java new file mode 100644 index 0000000000..bd30f98742 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest7.java @@ -0,0 +1,109 @@ +/* + * Copyright 1999-2011 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 junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser; +import com.alibaba.druid.sql.test.TestUtils; + +public class SQLServerSelectTest7 extends TestCase { + + public void test_isEmpty() throws Exception { + String sql = "with menu_view as(" + + "\n select t.*,1 level from sec_portal_menu t where t.parent_id = ?" + + "\n union all" + + "\n select t.*,level + 1 from sec_portal_menu t , menu_view x where t.parent_id = x.menu_id" + + "\n )" + + "\n select t.menu_id \"id\"," + + "\n t.menu_name \"name\"," + + "\n t.parent_id \"pId\"," + + "\n case t.level when 1 then 'true' else 'false' end \"open\"," + + "\n t.link_type \"linkType\"" + + "\n from menu_view t" + + "\n where 1=1" + + "\n and t.deleted = 0" + + "\n --菜单权限控制" + + "\n AND t.link_type in ('simple','link')" + + "\n AND (" + + "\n EXISTS (" + + "\n select p.entity_code from sec_role_auth p where p.entity_code = t.menu_id" + + "\n and p.entity_type = 'menu'" + + "\n and p.role_id in (" + + "\n select r.role_code from sec_role_member rm ,sec_role r where rm.entity_type = 'user'" + + "\n and entity_code = ? --用户ID" + + "\n and r.role_id = rm.role_id" + + "\n and r.enabled = 1" + + "\n and r.deleted = 0" + + "\n )" + + "\n )" + + "\n or '1'= ? --超级管理员账户id" + + "\n or t.need_control = 0" + + "\n )" + + "\n AND (" + + "\n t.enabled = 1 or '1'= ? --超级管理员账户id" + + "\n or t.need_control = 0" + + "\n )" + + "\n order by t.sort_order"; + + String expect = "WITH" + + "\n\tmenu_view" + + "\n\tAS" + + "\n\t(" + + "\n\t\tSELECT t.*, 1 AS level" + + "\n\t\tFROM sec_portal_menu t" + + "\n\t\tWHERE t.parent_id = ?" + + "\n\t\tUNION ALL" + + "\n\t\tSELECT t.*, level + 1" + + "\n\t\tFROM sec_portal_menu t, menu_view x" + + "\n\t\tWHERE t.parent_id = x.menu_id" + + "\n\t)" + + "\nSELECT t.menu_id AS \"id\", t.menu_name AS \"name\", t.parent_id AS \"pId\", CASE t.level WHEN 1 THEN 'true' ELSE 'false' END AS \"open\", t.link_type AS \"linkType\"" + + "\nFROM menu_view t" + + "\nWHERE 1 = 1" + + "\n\tAND t.deleted = 0" + + "\n\tAND t.link_type IN ('simple', 'link')" + + "\n\tAND (EXISTS (SELECT p.entity_code" + + "\n\t\t\tFROM sec_role_auth p" + + "\n\t\t\tWHERE p.entity_code = t.menu_id" + + "\n\t\t\t\tAND p.entity_type = 'menu'" + + "\n\t\t\t\tAND p.role_id IN (SELECT r.role_code" + + "\n\t\t\t\t\tFROM sec_role_member rm, sec_role r" + + "\n\t\t\t\t\tWHERE rm.entity_type = 'user'" + + "\n\t\t\t\t\t\tAND entity_code = ?" + + "\n\t\t\t\t\t\tAND r.role_id = rm.role_id" + + "\n\t\t\t\t\t\tAND r.enabled = 1" + + "\n\t\t\t\t\t\tAND r.deleted = 0))" + + "\n\t\tOR '1' = ?" + + "\n\t\tOR t.need_control = 0)" + + "\n\tAND (t.enabled = 1" + + "\n\t\tOR '1' = ?" + + "\n\t\tOR t.need_control = 0)" + + "\nORDER BY t.sort_order"; + + SQLServerStatementParser parser = new SQLServerStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + + String text = TestUtils.outputSqlServer(stmt); + + Assert.assertEquals(expect, text); + + System.out.println(text); + } +}