From 96c1950a8d11ca55b0b52dc605a07241cbf40fa9 Mon Sep 17 00:00:00 2001 From: wenshao Date: Wed, 21 Nov 2012 23:37:18 +0800 Subject: [PATCH] sql parser direct support OVER --- .../com/alibaba/druid/sql/ast/SQLOver.java | 74 +++++++++++++++++ .../druid/sql/ast/expr/SQLAggregateExpr.java | 25 ++++-- .../oracle/ast/expr/OracleAggregateExpr.java | 9 --- .../oracle/ast/expr/OracleAnalytic.java | 23 +----- .../postgresql/ast/PGAggregateExpr.java | 81 ------------------- .../postgresql/parser/PGExprParser.java | 62 +------------- .../dialect/postgresql/parser/PGLexer.java | 1 - .../postgresql/visitor/PGASTVisitor.java | 5 -- .../visitor/PGASTVisitorAdapter.java | 10 --- .../postgresql/visitor/PGOutputVisitor.java | 31 +------ .../visitor/PGSchemaStatVisitor.java | 11 --- .../sqlserver/parser/SQLServerExprParser.java | 12 +++ .../alibaba/druid/sql/parser/Keywords.java | 2 + .../druid/sql/parser/SQLExprParser.java | 31 +++++++ .../sql/visitor/SQLASTOutputVisitor.java | 18 +++++ .../druid/sql/visitor/SQLASTVisitor.java | 5 ++ .../sql/visitor/SQLASTVisitorAdapter.java | 10 +++ .../com/alibaba/druid/bvt/sql/OverTest.java | 40 +++++++++ .../sql/sqlserver/SQLServerRowNumberTest.java | 53 ++++++++++++ 19 files changed, 270 insertions(+), 233 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/sql/ast/SQLOver.java delete mode 100644 src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/PGAggregateExpr.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/OverTest.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerRowNumberTest.java diff --git a/src/main/java/com/alibaba/druid/sql/ast/SQLOver.java b/src/main/java/com/alibaba/druid/sql/ast/SQLOver.java new file mode 100644 index 0000000000..b7ef73fcfc --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/ast/SQLOver.java @@ -0,0 +1,74 @@ +package com.alibaba.druid.sql.ast; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class SQLOver extends SQLObjectImpl { + + private static final long serialVersionUID = 1L; + + protected final List partitionBy = new ArrayList(); + protected SQLOrderBy orderBy; + + @Override + protected void accept0(SQLASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, this.partitionBy); + acceptChild(visitor, this.orderBy); + } + visitor.endVisit(this); + } + + public SQLOrderBy getOrderBy() { + return orderBy; + } + + public void setOrderBy(SQLOrderBy orderBy) { + this.orderBy = orderBy; + } + + public List getPartitionBy() { + return partitionBy; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((orderBy == null) ? 0 : orderBy.hashCode()); + result = prime * result + ((partitionBy == null) ? 0 : partitionBy.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + SQLOver other = (SQLOver) obj; + if (orderBy == null) { + if (other.orderBy != null) { + return false; + } + } else if (!orderBy.equals(other.orderBy)) { + return false; + } + if (partitionBy == null) { + if (other.partitionBy != null) { + return false; + } + } else if (!partitionBy.equals(other.partitionBy)) { + return false; + } + return true; + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLAggregateExpr.java b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLAggregateExpr.java index d608cdb019..89c1fd6a0f 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLAggregateExpr.java +++ b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLAggregateExpr.java @@ -21,6 +21,7 @@ import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLExprImpl; +import com.alibaba.druid.sql.ast.SQLOver; import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class SQLAggregateExpr extends SQLExprImpl implements Serializable { @@ -29,6 +30,7 @@ public class SQLAggregateExpr extends SQLExprImpl implements Serializable { protected String methodName; protected Option option; protected final List arguments = new ArrayList(); + protected SQLOver over; public SQLAggregateExpr(String methodName){ @@ -60,20 +62,19 @@ public class SQLAggregateExpr extends SQLExprImpl implements Serializable { return this.arguments; } - public void output(StringBuffer buf) { - buf.append(this.methodName); - buf.append("("); - int i = 0; - for (int size = this.arguments.size(); i < size; ++i) { - ((SQLExpr) this.arguments.get(i)).output(buf); - } - buf.append(")"); + public SQLOver getOver() { + return over; + } + + public void setOver(SQLOver over) { + this.over = over; } @Override protected void accept0(SQLASTVisitor visitor) { if (visitor.visit(this)) { acceptChild(visitor, this.arguments); + acceptChild(visitor, this.over); } visitor.endVisit(this); @@ -86,6 +87,7 @@ public class SQLAggregateExpr extends SQLExprImpl implements Serializable { result = prime * result + ((arguments == null) ? 0 : arguments.hashCode()); result = prime * result + ((methodName == null) ? 0 : methodName.hashCode()); result = prime * result + ((option == null) ? 0 : option.hashCode()); + result = prime * result + ((over == null) ? 0 : over.hashCode()); return result; } @@ -115,6 +117,13 @@ public class SQLAggregateExpr extends SQLExprImpl implements Serializable { } else if (!methodName.equals(other.methodName)) { return false; } + if (over == null) { + if (other.over != null) { + return false; + } + } else if (!over.equals(other.over)) { + return false; + } if (option != other.option) { return false; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleAggregateExpr.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleAggregateExpr.java index ec22e01cda..6660323808 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleAggregateExpr.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleAggregateExpr.java @@ -25,7 +25,6 @@ import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class OracleAggregateExpr extends SQLAggregateExpr implements Serializable, OracleExpr { private static final long serialVersionUID = 1L; - private OracleAnalytic over; private boolean ignoreNulls = false; public boolean isUnique() { @@ -52,14 +51,6 @@ public class OracleAggregateExpr extends SQLAggregateExpr implements Serializabl super(methodName, option); } - public OracleAnalytic getOver() { - return this.over; - } - - public void setOver(OracleAnalytic over) { - this.over = over; - } - public void output(StringBuffer buf) { buf.append(this.methodName); buf.append("("); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleAnalytic.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleAnalytic.java index 5b0d6ce9ec..007f4fc6e7 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleAnalytic.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleAnalytic.java @@ -15,20 +15,13 @@ */ package com.alibaba.druid.sql.dialect.oracle.ast.expr; -import java.util.ArrayList; -import java.util.List; - -import com.alibaba.druid.sql.ast.SQLExpr; -import com.alibaba.druid.sql.ast.SQLObjectImpl; -import com.alibaba.druid.sql.dialect.oracle.ast.OracleOrderBy; +import com.alibaba.druid.sql.ast.SQLOver; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; -public class OracleAnalytic extends SQLObjectImpl implements OracleExpr { +public class OracleAnalytic extends SQLOver implements OracleExpr { private static final long serialVersionUID = 1L; - private final List partitionBy = new ArrayList(); - private OracleOrderBy orderBy; private OracleAnalyticWindowing windowing; public OracleAnalytic(){ @@ -49,14 +42,6 @@ public class OracleAnalytic extends SQLObjectImpl implements OracleExpr { visitor.endVisit(this); } - public OracleOrderBy getOrderBy() { - return this.orderBy; - } - - public void setOrderBy(OracleOrderBy orderBy) { - this.orderBy = orderBy; - } - public OracleAnalyticWindowing getWindowing() { return this.windowing; } @@ -64,8 +49,4 @@ public class OracleAnalytic extends SQLObjectImpl implements OracleExpr { public void setWindowing(OracleAnalyticWindowing windowing) { this.windowing = windowing; } - - public List getPartitionBy() { - return this.partitionBy; - } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/PGAggregateExpr.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/PGAggregateExpr.java deleted file mode 100644 index 6f4ac99afd..0000000000 --- a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/PGAggregateExpr.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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.sql.dialect.postgresql.ast; - -import com.alibaba.druid.sql.ast.SQLExpr; -import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; -import com.alibaba.druid.sql.dialect.postgresql.ast.expr.PGAnalytic; -import com.alibaba.druid.sql.dialect.postgresql.visitor.PGASTVisitor; -import com.alibaba.druid.sql.visitor.SQLASTVisitor; - -public class PGAggregateExpr extends SQLAggregateExpr implements PGSQLObject { - - private PGAnalytic over; - - public PGAnalytic getOver() { - return over; - } - - public void setOver(PGAnalytic over) { - this.over = over; - } - - private static final long serialVersionUID = 1L; - - public PGAggregateExpr(String methodName, Option option) { - super(methodName, option); - } - - public PGAggregateExpr(String methodName) { - super(methodName); - } - - public void output(StringBuffer buf) { - buf.append(this.methodName); - buf.append("("); - int i = 0; - for (int size = this.arguments.size(); i < size; ++i) { - ((SQLExpr) this.arguments.get(i)).output(buf); - } - buf.append(")"); - - if (this.over != null) { - buf.append(" "); - this.over.output(buf); - } - } - - @Override - protected void accept0(SQLASTVisitor visitor) { - if (visitor instanceof PGASTVisitor) { - this.accept0((PGASTVisitor) visitor); - } else { - if (visitor.visit(this)) { - acceptChild(visitor, this.arguments); - acceptChild(visitor, this.over); - } - visitor.endVisit(this); - } - } - - public void accept0(PGASTVisitor visitor) { - if (visitor.visit(this)) { - acceptChild(visitor, this.arguments); - acceptChild(visitor, this.over); - } - visitor.endVisit(this); - } -} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGExprParser.java index da82e8deba..5a50b36e6f 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGExprParser.java @@ -15,16 +15,14 @@ */ package com.alibaba.druid.sql.dialect.postgresql.parser; -import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; -import com.alibaba.druid.sql.dialect.postgresql.ast.PGAggregateExpr; import com.alibaba.druid.sql.dialect.postgresql.ast.PGOrderBy; -import com.alibaba.druid.sql.dialect.postgresql.ast.expr.PGAnalytic; import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.SQLExprParser; import com.alibaba.druid.sql.parser.Token; -public class PGExprParser extends SQLExprParser{ - public PGExprParser(String sql) { +public class PGExprParser extends SQLExprParser { + + public PGExprParser(String sql){ super(new PGLexer(sql)); this.lexer.nextToken(); } @@ -32,63 +30,11 @@ public class PGExprParser extends SQLExprParser{ public PGExprParser(Lexer lexer){ super(lexer); } - - protected SQLAggregateExpr parseAggregateExpr(String methodName) { - methodName = methodName.toUpperCase(); - - PGAggregateExpr aggregateExpr; - if (lexer.token() == Token.ALL) { - aggregateExpr = new PGAggregateExpr(methodName, SQLAggregateExpr.Option.ALL); - lexer.nextToken(); - } else if (lexer.token() == Token.DISTINCT) { - aggregateExpr = new PGAggregateExpr(methodName, SQLAggregateExpr.Option.DISTINCT); - lexer.nextToken(); - } else { - aggregateExpr = new PGAggregateExpr(methodName); - } - exprList(aggregateExpr.getArguments()); - - accept(Token.RPAREN); - - if (lexer.token() == Token.OVER) { - lexer.nextToken(); - PGAnalytic over = new PGAnalytic(); - accept(Token.LPAREN); - - if (identifierEquals("PARTITION")) { - lexer.nextToken(); - accept(Token.BY); - - if (lexer.token() == (Token.LPAREN)) { - lexer.nextToken(); - exprList(over.getPartitionBy()); - accept(Token.RPAREN); - } else { - exprList(over.getPartitionBy()); - } - } - - - over.setOrderBy(parseOrderBy()); - - // if (over.getOrderBy() != null) { - // //TODO window - // } - - accept(Token.RPAREN); - aggregateExpr.setOver(over); - - } - - return aggregateExpr; - } - - @Override public PGOrderBy parseOrderBy() { if (lexer.token() == (Token.ORDER)) { - PGOrderBy orderBy = new PGOrderBy(); + PGOrderBy orderBy = new PGOrderBy(); lexer.nextToken(); if (identifierEquals("SIBLINGS")) { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGLexer.java index 590caa6016..740f96e877 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGLexer.java @@ -47,7 +47,6 @@ public class PGLexer extends Lexer { map.put("OFFSET", Token.OFFSET); map.put("ONLY", Token.ONLY); - map.put("OVER", Token.OVER); map.put("RECURSIVE", Token.RECURSIVE); map.put("RESTART", Token.RESTART); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGASTVisitor.java index 8722699862..195b6a35e3 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGASTVisitor.java @@ -15,7 +15,6 @@ */ package com.alibaba.druid.sql.dialect.postgresql.visitor; -import com.alibaba.druid.sql.dialect.postgresql.ast.PGAggregateExpr; import com.alibaba.druid.sql.dialect.postgresql.ast.PGWithClause; import com.alibaba.druid.sql.dialect.postgresql.ast.PGWithQuery; import com.alibaba.druid.sql.dialect.postgresql.ast.expr.PGAnalytic; @@ -75,10 +74,6 @@ public interface PGASTVisitor extends SQLASTVisitor { boolean visit(PGUpdateStatement x); - void endVisit(PGAggregateExpr x); - - boolean visit(PGAggregateExpr x); - void endVisit(PGAnalytic x); boolean visit(PGAnalytic x); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGASTVisitorAdapter.java index bff1bf4f35..62da7ab822 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGASTVisitorAdapter.java @@ -15,7 +15,6 @@ */ package com.alibaba.druid.sql.dialect.postgresql.visitor; -import com.alibaba.druid.sql.dialect.postgresql.ast.PGAggregateExpr; import com.alibaba.druid.sql.dialect.postgresql.ast.PGWithClause; import com.alibaba.druid.sql.dialect.postgresql.ast.PGWithQuery; import com.alibaba.druid.sql.dialect.postgresql.ast.expr.PGAnalytic; @@ -151,15 +150,6 @@ public class PGASTVisitorAdapter extends SQLASTVisitorAdapter implements PGASTVi return true; } - @Override - public void endVisit(PGAggregateExpr x) { - } - - @Override - public boolean visit(PGAggregateExpr x) { - return true; - } - @Override public void endVisit(PGAnalytic x) { } 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 8b3aa954a8..b9bea852e3 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 @@ -18,7 +18,6 @@ package com.alibaba.druid.sql.dialect.postgresql.visitor; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLSetQuantifier; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; -import com.alibaba.druid.sql.dialect.postgresql.ast.PGAggregateExpr; import com.alibaba.druid.sql.dialect.postgresql.ast.PGWithClause; import com.alibaba.druid.sql.dialect.postgresql.ast.PGWithQuery; import com.alibaba.druid.sql.dialect.postgresql.ast.expr.PGAnalytic; @@ -440,32 +439,6 @@ public class PGOutputVisitor extends SQLASTOutputVisitor implements PGASTVisitor } - @Override - public void endVisit(PGAggregateExpr x) { - - } - - @Override - public boolean visit(PGAggregateExpr x) { - print(x.getMethodName()); - - print("("); - - if (x.getOption() != null) { - print(x.getOption().toString()); - print(' '); - } - - printAndAccept(x.getArguments(), ", "); - print(")"); - - if (x.getOver() != null) { - x.getOver().accept(this); - } - - return false; - } - @Override public void endVisit(PGAnalytic x) { @@ -513,7 +486,7 @@ public class PGOutputVisitor extends SQLASTOutputVisitor implements PGASTVisitor print(" AS "); print(x.getAlias()); } - + if (x.getParameters().size() > 0) { print('('); printAndAccept(x.getParameters(), ", "); @@ -525,7 +498,7 @@ public class PGOutputVisitor extends SQLASTOutputVisitor implements PGASTVisitor @Override public void endVisit(PGFunctionTableSource x) { - + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGSchemaStatVisitor.java index 5e950c923f..9b31a744e5 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGSchemaStatVisitor.java @@ -22,7 +22,6 @@ import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.ast.statement.SQLTruncateStatement; -import com.alibaba.druid.sql.dialect.postgresql.ast.PGAggregateExpr; import com.alibaba.druid.sql.dialect.postgresql.ast.PGWithClause; import com.alibaba.druid.sql.dialect.postgresql.ast.PGWithQuery; import com.alibaba.druid.sql.dialect.postgresql.ast.expr.PGAnalytic; @@ -260,16 +259,6 @@ public class PGSchemaStatVisitor extends SchemaStatVisitor implements PGASTVisit return this.visit((SQLSelectQueryBlock) x); } - @Override - public void endVisit(PGAggregateExpr x) { - - } - - @Override - public boolean visit(PGAggregateExpr x) { - return false; - } - @Override public void endVisit(PGAnalytic x) { 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 45e72d31fb..f7a70e4c2b 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 @@ -94,4 +94,16 @@ public class SQLServerExprParser extends SQLExprParser { return super.nameRest(expr); } + + public boolean isAggreateFunction(String word) { + String[] aggregateFunctions = { "AVG", "COUNT", "MAX", "MIN", "ROW_NUMBER", "STDDEV", "SUM" }; + + for (int i = 0; i < aggregateFunctions.length; ++i) { + if (aggregateFunctions[i].compareToIgnoreCase(word) == 0) { + return true; + } + } + + return false; + } } diff --git a/src/main/java/com/alibaba/druid/sql/parser/Keywords.java b/src/main/java/com/alibaba/druid/sql/parser/Keywords.java index 2daf5327e0..116a740c14 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Keywords.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Keywords.java @@ -111,6 +111,8 @@ public class Keywords { map.put("WHEN", Token.WHEN); map.put("WHERE", Token.WHERE); map.put("XOR", Token.XOR); + + map.put("OVER", Token.OVER); DEFAULT_KEYWORDS = new Keywords(map); } 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 6bea9cafd4..a97aa522e3 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java @@ -27,6 +27,7 @@ import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.SQLOrderingSpecification; +import com.alibaba.druid.sql.ast.SQLOver; import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; import com.alibaba.druid.sql.ast.expr.SQLAllExpr; @@ -696,6 +697,36 @@ public class SQLExprParser extends SQLParser { exprList(aggregateExpr.getArguments()); accept(Token.RPAREN); + + if (lexer.token() == Token.OVER) { + lexer.nextToken(); + SQLOver over = new SQLOver(); + accept(Token.LPAREN); + + if (identifierEquals("PARTITION")) { + lexer.nextToken(); + accept(Token.BY); + + if (lexer.token() == (Token.LPAREN)) { + lexer.nextToken(); + exprList(over.getPartitionBy()); + accept(Token.RPAREN); + } else { + exprList(over.getPartitionBy()); + } + } + + + over.setOrderBy(parseOrderBy()); + + // if (over.getOrderBy() != null) { + // //TODO window + // } + + accept(Token.RPAREN); + aggregateExpr.setOver(over); + + } return aggregateExpr; } 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 e510f8bde8..48619a6b58 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -24,6 +24,7 @@ import com.alibaba.druid.sql.ast.SQLDataType; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLObject; import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.SQLOver; import com.alibaba.druid.sql.ast.SQLSetQuantifier; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; @@ -451,6 +452,11 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter { printAndAccept(x.getArguments(), ", "); print(")"); + + if (x.getOver() != null) { + print(" "); + x.getOver().accept(this); + } return false; } @@ -1161,4 +1167,16 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter { x.getPrimaryKey().accept(this); return false; } + + @Override + public boolean visit(SQLOver x) { + print("OVER ("); + printAndAccept(x.getPartitionBy(), ", "); + print(")"); + if (x.getOrderBy() != null) { + print(" "); + x.getOrderBy().accept(this); + } + return false; + } } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java index 20061a4566..fb3881874b 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java @@ -19,6 +19,7 @@ import com.alibaba.druid.sql.ast.SQLCommentHint; import com.alibaba.druid.sql.ast.SQLDataType; import com.alibaba.druid.sql.ast.SQLObject; import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.SQLOver; import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; import com.alibaba.druid.sql.ast.expr.SQLAllExpr; @@ -390,4 +391,8 @@ public interface SQLASTVisitor { void endVisit(SQLCreateDatabaseStatement x); boolean visit(SQLCreateDatabaseStatement x); + + void endVisit(SQLOver x); + + boolean visit(SQLOver x); } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java index 7bd9b7f950..374e79de58 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java @@ -19,6 +19,7 @@ import com.alibaba.druid.sql.ast.SQLCommentHint; import com.alibaba.druid.sql.ast.SQLDataType; import com.alibaba.druid.sql.ast.SQLObject; import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.SQLOver; import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; import com.alibaba.druid.sql.ast.expr.SQLAllExpr; @@ -704,4 +705,13 @@ public class SQLASTVisitorAdapter implements SQLASTVisitor { public void endVisit(SQLAlterTableAddPrimaryKey x) { } + + @Override + public void endVisit(SQLOver x) { + } + + @Override + public boolean visit(SQLOver x) { + return true; + } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/OverTest.java b/src/test/java/com/alibaba/druid/bvt/sql/OverTest.java new file mode 100644 index 0000000000..3bdc7137a1 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/OverTest.java @@ -0,0 +1,40 @@ +package com.alibaba.druid.bvt.sql; + +import java.util.List; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.parser.SQLStatementParser; +import com.alibaba.druid.sql.visitor.SchemaStatVisitor; + +public class OverTest extends TestCase { + + public void test_over() throws Exception { + String sql = "SELECT SalesOrderID, ProductID, OrderQty" + // + " ,SUM(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Total'" + // + " ,AVG(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Avg'" + // + " ,COUNT(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Count'" + // + " ,MIN(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Min'" + // + " ,MAX(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Max' " + // + "FROM Sales.SalesOrderDetail " + // + "WHERE SalesOrderID IN(43659,43664);"; + + SQLStatementParser parser = new SQLStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + + Assert.assertEquals(1, statementList.size()); + + SchemaStatVisitor visitor = new SchemaStatVisitor(); + statemen.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + + Assert.assertEquals(3, visitor.getColumns().size()); + Assert.assertEquals(1, visitor.getTables().size()); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerRowNumberTest.java b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerRowNumberTest.java new file mode 100644 index 0000000000..ac3853e475 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerRowNumberTest.java @@ -0,0 +1,53 @@ +/* + * 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.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 SQLServerRowNumberTest extends TestCase { + + public void test_isEmpty() throws Exception { + String sql = "SELECT c.FirstName, c.LastName" + // + " ,ROW_NUMBER() OVER(ORDER BY SalesYTD DESC) AS 'Row Number'" + // + " ,s.SalesYTD, a.PostalCode " + // + "FROM Sales.SalesPerson s " + // + " INNER JOIN Person.Contact c " + // + " ON s.SalesPersonID = c.ContactID" + // + " INNER JOIN Person.Address a " + // + " ON a.AddressID = c.ContactID " + // + "WHERE TerritoryID IS NOT NULL " + // + " AND SalesYTD <> 0;"; + + String expect = "SELECT c.FirstName, c.LastName, ROW_NUMBER() OVER () ORDER BY SalesYTD DESC AS 'Row Number', s.SalesYTD, a.PostalCode" // + + "\nFROM Sales.SalesPerson s INNER JOIN Person.Contact c ON s.SalesPersonID = c.ContactID INNER JOIN Person.Address a ON a.AddressID = c.ContactID" // + + "\nWHERE TerritoryID IS NOT NULL" // + + "\n\tAND SalesYTD <> 0"; + + SQLServerStatementParser parser = new SQLServerStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + + String text = TestUtils.outputSqlServer(stmt); + + Assert.assertEquals(expect, text); + + System.out.println(text); + } +}