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 new file mode 100644 index 0000000000..390aed24a9 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/PGAggregateExpr.java @@ -0,0 +1,66 @@ +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, int 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/ast/PGAnalyticWindowing.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/PGAnalyticWindowing.java new file mode 100644 index 0000000000..852a93b62e --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/PGAnalyticWindowing.java @@ -0,0 +1,67 @@ +/* + * 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.SQLObjectImpl; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; +import com.alibaba.druid.sql.dialect.postgresql.ast.expr.PGExpr; +import com.alibaba.druid.sql.dialect.postgresql.visitor.PGASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +@SuppressWarnings("serial") +public class PGAnalyticWindowing extends SQLObjectImpl implements PGExpr { + + private Type type; + private SQLExpr expr; + + public PGAnalyticWindowing(){ + + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + this.accept0((PGASTVisitor) visitor); + } + + public void accept0(PGASTVisitor visitor) { + //TODO +// if (visitor.visit(this)) { +// acceptChild(visitor, this.expr); +// } +// visitor.endVisit(this); + } + + public SQLExpr getExpr() { + return this.expr; + } + + public void setExpr(SQLExpr expr) { + this.expr = expr; + } + + public Type getType() { + return this.type; + } + + public void setType(Type type) { + this.type = type; + } + + public static enum Type { + ROWS, RANGE; + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/PGOrderBy.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/PGOrderBy.java new file mode 100644 index 0000000000..636f31646f --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/PGOrderBy.java @@ -0,0 +1,53 @@ +package com.alibaba.druid.sql.dialect.postgresql.ast; + +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.dialect.postgresql.visitor.PGASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class PGOrderBy extends SQLOrderBy { + private static final long serialVersionUID = 1L; + + private boolean sibings; + + + + public boolean isSibings() { + return this.sibings; + } + + public void setSibings(boolean sibings) { + this.sibings = sibings; + } + + protected void accept0(SQLASTVisitor visitor) { + if (visitor instanceof PGASTVisitor) { + accept0((PGASTVisitor) visitor); + } else { + super.accept0(visitor); + } + } + + protected void accept0(PGASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, this.items); + } + + visitor.endVisit(this); + } + + public void output(StringBuffer buf) { + buf.append("ORDER "); + if (this.sibings) { + buf.append("SIBLINGS "); + } + buf.append("BY "); + + int i = 0; + for (int size = this.items.size(); i < size; ++i) { + if (i != 0) { + buf.append(", "); + } + ((PGOrderByItem) this.items.get(i)).output(buf); + } + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/PGOrderByItem.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/PGOrderByItem.java new file mode 100644 index 0000000000..5994489ff8 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/PGOrderByItem.java @@ -0,0 +1,87 @@ +/* + * 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.SQLOrderingSpecification; +import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem; +import com.alibaba.druid.sql.dialect.postgresql.visitor.PGASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class PGOrderByItem extends SQLSelectOrderByItem { + + private static final long serialVersionUID = 1L; + + private NullsOrderType nullsOrderType; + + public PGOrderByItem(){ + + } + + public NullsOrderType getNullsOrderType() { + return this.nullsOrderType; + } + + public void setNullsOrderType(NullsOrderType nullsOrderType) { + this.nullsOrderType = nullsOrderType; + } + + protected void accept0(SQLASTVisitor visitor) { + if (visitor instanceof PGASTVisitor) { + accept0((PGASTVisitor) visitor); + } else { + super.accept0(visitor); + } + } + + protected void accept0(PGASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, this.expr); + } + + visitor.endVisit(this); + } + + public void output(StringBuffer buf) { + this.expr.output(buf); + if (SQLOrderingSpecification.ASC.equals(this.type)) { + buf.append(" ASC"); + } else if (SQLOrderingSpecification.DESC.equals(this.type)) { + buf.append(" DESC"); + } + if (NullsOrderType.NullsFirst.equals(this.nullsOrderType)) { + buf.append(" NULLS FIRST"); + } else if (NullsOrderType.NullsLast.equals(this.nullsOrderType)) { + buf.append(" NULLS LAST"); + } + } + + public static enum NullsOrderType { + NullsFirst, NullsLast; + + public String toFormalString() { + if (NullsFirst.equals(this)) { + return "NULLS FIRST"; + } + + if (NullsLast.equals(this)) { + return "NULLS LAST"; + } + + throw new IllegalArgumentException(); + } + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/expr/PGAnalytic.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/expr/PGAnalytic.java new file mode 100644 index 0000000000..ee8c572f11 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/expr/PGAnalytic.java @@ -0,0 +1,64 @@ +/* + * 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.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.postgresql.ast.PGAnalyticWindowing; +import com.alibaba.druid.sql.dialect.postgresql.ast.PGOrderBy; +import com.alibaba.druid.sql.dialect.postgresql.visitor.PGASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class PGAnalytic extends SQLObjectImpl implements PGExpr { + + private static final long serialVersionUID = 1L; + private final List partitionBy = new ArrayList(); + private PGOrderBy orderBy; + + + public PGOrderBy getOrderBy() { + return orderBy; + } + + public void setOrderBy(PGOrderBy orderBy) { + this.orderBy = orderBy; + } + + public PGAnalytic(){ + + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + this.accept0((PGASTVisitor) visitor); + } + + public void accept0(PGASTVisitor visitor) { +// if (visitor.visit(this)) { +// acceptChild(visitor, this.partitionBy); +// acceptChild(visitor, this.orderBy); +// } +// visitor.endVisit(this); + } + + + public List getPartitionBy() { + return this.partitionBy; + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/expr/PGExpr.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/expr/PGExpr.java new file mode 100644 index 0000000000..7d08a58e5b --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/expr/PGExpr.java @@ -0,0 +1,8 @@ +package com.alibaba.druid.sql.dialect.postgresql.ast.expr; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.dialect.postgresql.ast.PGSQLObject; + +public interface PGExpr extends SQLExpr, PGSQLObject{ + +} 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 new file mode 100644 index 0000000000..945a2393c0 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGExprParser.java @@ -0,0 +1,99 @@ +package com.alibaba.druid.sql.dialect.postgresql.parser; + +import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleOrderBy; +import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalyticWindowing; +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.ParserException; +import com.alibaba.druid.sql.parser.SQLExprParser; +import com.alibaba.druid.sql.parser.Token; + +public class PGExprParser extends SQLExprParser{ + public PGExprParser(String sql) throws ParserException{ + super(sql); + } + + public PGExprParser(Lexer lexer){ + super(lexer); + } + + protected SQLAggregateExpr parseAggregateExpr(String method_name) throws ParserException { + PGAggregateExpr aggregateExpr; + if (lexer.token() == Token.ALL) { + aggregateExpr = new PGAggregateExpr(method_name, 1); + lexer.nextToken(); + } else if (lexer.token() == Token.DISTINCT) { + aggregateExpr = new PGAggregateExpr(method_name, 0); + lexer.nextToken(); + } else { + aggregateExpr = new PGAggregateExpr(method_name, 1); + } + + 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(); + lexer.nextToken(); + + if (identifierEquals("SIBLINGS")) { + lexer.nextToken(); + orderBy.setSibings(true); + } + + accept(Token.BY); + + orderBy.getItems().add(parseSelectOrderByItem()); + + while (lexer.token() == (Token.COMMA)) { + lexer.nextToken(); + orderBy.getItems().add(parseSelectOrderByItem()); + } + + return orderBy; + } + + return null; + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/proxy/filter/MergeStatFilterTest3.java b/src/test/java/com/alibaba/druid/bvt/proxy/filter/MergeStatFilterTest3.java new file mode 100644 index 0000000000..f74fa25fa7 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/proxy/filter/MergeStatFilterTest3.java @@ -0,0 +1,48 @@ +package com.alibaba.druid.bvt.proxy.filter; + +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.Statement; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.util.IOUtils; + +public class MergeStatFilterTest3 extends TestCase { + private DruidDataSource dataSource; + + protected void setUp() throws Exception { + dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:mock:xx"); + dataSource.setFilters("mergeStat"); + dataSource.setDbType("postgresql"); + } + + protected void tearDown() throws Exception { + dataSource.close(); + } + + public void test_merge() throws Exception { + String sql = IOUtils.read(new InputStreamReader(this.getClass().getResourceAsStream("/bvt/parser/postgresql.txt"))); + //System.out.println(sql); + { + Connection conn = dataSource.getConnection(); + + Statement stmt = conn.createStatement(); + stmt.execute(sql); + stmt.close(); + conn.close(); + } + + + Assert.assertEquals(1, dataSource.getDataSourceStat().getSqlStatMap() + .size()); + System.out.println(dataSource.getDataSourceStat().getSqlStatMap().keySet().iterator().next()); + System.out.println("---------------------------------------------------"); + System.out.println(SQLUtils.formatPGSql(sql)); + } + +} diff --git a/src/test/resources/bvt/parser/postgresql.txt b/src/test/resources/bvt/parser/postgresql.txt new file mode 100644 index 0000000000..cc29107e67 --- /dev/null +++ b/src/test/resources/bvt/parser/postgresql.txt @@ -0,0 +1,19 @@ +/*!cobar: select,4,ireport.dm_mdm_mem_prod_effect_sdt0.admin_member_seq=201152175*/ + select product_id, + sum_prod_show_num, + sum_prod_click_num, + sum_prod_fb_num, + total_cnt + from (select product_id, + sum_prod_show_num, + sum_prod_click_num, + sum_prod_fb_num, + count(*) over(order by a,b,c) as total_cnt + from (select product_id, + sum_prod_show_num, + sum_prod_click_num, + sum_prod_fb_num + from ireport.dm_mdm_mem_prod_effect_sdt0 + where stat_date = '2012-02-19' + and admin_member_seq = ?) b + Order by sum_prod_show_num desc, product_id desc) a limit ? offset (? -1) * ?