diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/expr/SQLServerObjectReferenceExpr.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/expr/SQLServerObjectReferenceExpr.java index c2d6de348b..b8cd6e3477 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/expr/SQLServerObjectReferenceExpr.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/expr/SQLServerObjectReferenceExpr.java @@ -1,9 +1,13 @@ package com.alibaba.druid.sql.dialect.sqlserver.ast.expr; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerObjectImpl; import com.alibaba.druid.sql.dialect.sqlserver.visitor.SQLServerASTVisitor; -public class SQLServerObjectReferenceExpr extends SQLServerObjectImpl implements SQLServerExpr { +public class SQLServerObjectReferenceExpr extends SQLServerObjectImpl implements SQLServerExpr, SQLName { private static final long serialVersionUID = 1L; @@ -11,10 +15,32 @@ public class SQLServerObjectReferenceExpr extends SQLServerObjectImpl implements private String database; private String schema; private String object; - + + public SQLServerObjectReferenceExpr() { + + } + + public SQLServerObjectReferenceExpr(SQLExpr owner, String name) { + if (owner instanceof SQLIdentifierExpr) { + this.database = ((SQLIdentifierExpr) owner).getName(); + this.object = name; + } else if (owner instanceof SQLPropertyExpr) { + SQLPropertyExpr propExpr = (SQLPropertyExpr) owner; + + this.server = ((SQLIdentifierExpr) propExpr.getOwner()).getName(); + this.database = propExpr.getName(); + this.object = name; + } else { + throw new IllegalArgumentException(owner.toString()); + } + } + @Override public void accept0(SQLServerASTVisitor visitor) { - + if (visitor.visit(this)) { + + } + visitor.endVisit(this); } public void output(StringBuffer buf) { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerExprParser.java index cc955706f2..278b5b61b1 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerExprParser.java @@ -1,8 +1,12 @@ package com.alibaba.druid.sql.dialect.sqlserver.parser; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.dialect.sqlserver.ast.expr.SQLServerObjectReferenceExpr; import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.ParserException; import com.alibaba.druid.sql.parser.SQLExprParser; +import com.alibaba.druid.sql.parser.Token; public class SQLServerExprParser extends SQLExprParser { @@ -15,4 +19,23 @@ public class SQLServerExprParser extends SQLExprParser { this.lexer.nextToken(); } + public SQLExpr primaryRest(SQLExpr expr) throws ParserException { + if (lexer.token() == Token.DOTDOT) { + expr = nameRest((SQLName) expr); + } + + return super.primaryRest(expr); + } + + public SQLName nameRest(SQLName name) throws ParserException { + if (lexer.token() == Token.DOTDOT) { + lexer.nextToken(); + String text = lexer.stringVal(); + lexer.nextToken(); + + name = new SQLServerObjectReferenceExpr(name, text); + } + + return super.nameRest(name); + } } 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 d88a79ec49..c3cd4d58bb 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 @@ -8,6 +8,7 @@ import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.Top; import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.ParserException; +import com.alibaba.druid.sql.parser.SQLExprParser; import com.alibaba.druid.sql.parser.SQLSelectParser; import com.alibaba.druid.sql.parser.Token; @@ -76,4 +77,8 @@ public class SQLServerSelectParser extends SQLSelectParser { return queryRest(queryBlock); } + + protected SQLExprParser createExprParser() { + return new SQLServerExprParser(lexer); + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitor.java index 30133fe68c..62ffab2bcc 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitor.java @@ -2,6 +2,7 @@ package com.alibaba.druid.sql.dialect.sqlserver.visitor; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.Top; +import com.alibaba.druid.sql.dialect.sqlserver.ast.expr.SQLServerObjectReferenceExpr; import com.alibaba.druid.sql.visitor.SQLASTVisitor; public interface SQLServerASTVisitor extends SQLASTVisitor { @@ -13,4 +14,8 @@ public interface SQLServerASTVisitor extends SQLASTVisitor { boolean visit(Top x); void endVisit(Top x); + + boolean visit(SQLServerObjectReferenceExpr x); + + void endVisit(SQLServerObjectReferenceExpr x); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitorAdapter.java index 9eff67f70d..3e917ed396 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitorAdapter.java @@ -2,6 +2,7 @@ package com.alibaba.druid.sql.dialect.sqlserver.visitor; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.Top; +import com.alibaba.druid.sql.dialect.sqlserver.ast.expr.SQLServerObjectReferenceExpr; import com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter; public class SQLServerASTVisitorAdapter extends SQLASTVisitorAdapter implements SQLServerASTVisitor { @@ -25,5 +26,15 @@ public class SQLServerASTVisitorAdapter extends SQLASTVisitorAdapter implements public void endVisit(Top x) { } + + @Override + public boolean visit(SQLServerObjectReferenceExpr x) { + return true; + } + + @Override + public void endVisit(SQLServerObjectReferenceExpr x) { + + } } 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 5d38004f23..f10368e017 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 @@ -3,6 +3,7 @@ package com.alibaba.druid.sql.dialect.sqlserver.visitor; import com.alibaba.druid.sql.ast.SQLSetQuantifier; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.Top; +import com.alibaba.druid.sql.dialect.sqlserver.ast.expr.SQLServerObjectReferenceExpr; import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; public class SQLServerOutputVisitor extends SQLASTOutputVisitor implements SQLServerASTVisitor { @@ -42,7 +43,7 @@ public class SQLServerOutputVisitor extends SQLASTOutputVisitor implements SQLSe } if (x.getGroupBy() != null) { - print(" "); + println(); x.getGroupBy().accept(this); } @@ -67,4 +68,15 @@ public class SQLServerOutputVisitor extends SQLASTOutputVisitor implements SQLSe } + @Override + public boolean visit(SQLServerObjectReferenceExpr x) { + print(x.toString()); + return false; + } + + @Override + public void endVisit(SQLServerObjectReferenceExpr x) { + + } + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerSchemaStatVisitor.java index aafc17d44a..aff689e0d8 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerSchemaStatVisitor.java @@ -3,6 +3,7 @@ package com.alibaba.druid.sql.dialect.sqlserver.visitor; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.Top; +import com.alibaba.druid.sql.dialect.sqlserver.ast.expr.SQLServerObjectReferenceExpr; import com.alibaba.druid.sql.visitor.SchemaStatVisitor; @@ -28,5 +29,15 @@ public class SQLServerSchemaStatVisitor extends SchemaStatVisitor implements SQL } + @Override + public boolean visit(SQLServerObjectReferenceExpr x) { + return false; + } + + @Override + public void endVisit(SQLServerObjectReferenceExpr x) { + + } + } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java index c9dd9a68eb..92ae4c8f12 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java @@ -626,7 +626,13 @@ public class SQLExprParser extends SQLParser { SQLName name = new SQLIdentifierExpr(identName); - while (lexer.token() == Token.DOT) { + name = nameRest(name); + + return name; + } + + public SQLName nameRest(SQLName name) throws ParserException { + if (lexer.token() == Token.DOT) { lexer.nextToken(); if (lexer.token() == Token.KEY) { @@ -646,6 +652,7 @@ public class SQLExprParser extends SQLParser { name = new SQLPropertyExpr(name, lexer.stringVal()); } lexer.nextToken(); + name = nameRest(name); } return name; 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 d1179a16d2..3e21457307 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -494,7 +494,7 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter { x.getQuery().accept(this); if (x.getOrderBy() != null) { - print(" "); + println(); x.getOrderBy().accept(this); } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest2.java b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest2.java new file mode 100644 index 0000000000..21c1c5c0f0 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest2.java @@ -0,0 +1,31 @@ +package com.alibaba.druid.bvt.sql.sqlserver; + +import junit.framework.Assert; +import junit.framework.TestCase; + +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 SQLServerSelectTest2 extends TestCase { + + public void test_isEmpty() throws Exception { + String sql = "SELECT TOP 1 name FROM (SELECT TOP 9 name FROM master..syslogins ORDER BY name ASC) sq ORDER BY name DESC "; + + String expect = "SELECT TOP 1 name\n" + // + "FROM (SELECT TOP 9 name\n" + // + "\tFROM master..syslogins\n" + // + "\tORDER BY name ASC\n" + // + "\t) sq\n" + // + "ORDER BY name DESC"; + + SQLServerStatementParser parser = new SQLServerStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + + String text = TestUtils.outputSqlServer(stmt); + + Assert.assertEquals(expect, text); + + System.out.println(text); + } +}