From 0583a060cda6517bc9ff9f9025e4e4840a9a141a Mon Sep 17 00:00:00 2001 From: Yako Date: Tue, 30 Dec 2014 23:58:49 +0800 Subject: [PATCH] support odps select ... limit... --- .../java/com/alibaba/druid/sql/SQLUtils.java | 4 + .../odps/ast/OdpsSelectQueryBlock.java | 97 +++++++++++++++++++ .../sql/dialect/odps/parser/OdpsLexer.java | 1 + .../dialect/odps/parser/OdpsSelectParser.java | 76 ++++++++++++--- .../odps/parser/OdpsStatementParser.java | 13 ++- .../dialect/odps/visitor/OdpsASTVisitor.java | 5 + .../odps/visitor/OdpsOutputVisitor.java | 96 +++++++++++++++--- .../odps/visitor/OdpsSchemaStatVisitor.java | 34 ++++--- .../druid/sql/parser/SQLParserUtils.java | 5 + 9 files changed, 291 insertions(+), 40 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsSelectQueryBlock.java diff --git a/src/main/java/com/alibaba/druid/sql/SQLUtils.java b/src/main/java/com/alibaba/druid/sql/SQLUtils.java index a11fef46f2..49913c7e7b 100644 --- a/src/main/java/com/alibaba/druid/sql/SQLUtils.java +++ b/src/main/java/com/alibaba/druid/sql/SQLUtils.java @@ -241,6 +241,10 @@ public class SQLUtils { if (JdbcUtils.DB2.equals(dbType)) { return new DB2OutputVisitor(out); } + + if (JdbcUtils.ODPS.equals(dbType)) { + return new OdpsOutputVisitor(out); + } return new SQLASTOutputVisitor(out); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsSelectQueryBlock.java new file mode 100644 index 0000000000..8adfa9c2b8 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsSelectQueryBlock.java @@ -0,0 +1,97 @@ +/* + * 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.odps.ast; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.dialect.odps.visitor.OdpsASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class OdpsSelectQueryBlock extends SQLSelectQueryBlock { + + private SQLOrderBy orderBy; + + private SQLExpr limit; + + public OdpsSelectQueryBlock(){ + + } + + public SQLOrderBy getOrderBy() { + return orderBy; + } + + public void setOrderBy(SQLOrderBy orderBy) { + this.orderBy = orderBy; + } + + public SQLExpr getLimit() { + return limit; + } + + public void setLimit(SQLExpr limit) { + if (limit != null) { + limit.setParent(this); + } + this.limit = limit; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((limit == null) ? 0 : limit.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; + OdpsSelectQueryBlock other = (OdpsSelectQueryBlock) obj; + if (limit == null) { + if (other.limit != null) return false; + } else if (!limit.equals(other.limit)) return false; + return true; + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + if (visitor instanceof OdpsASTVisitor) { + accept0((OdpsASTVisitor) visitor); + return; + } + + super.accept0(visitor); + } + + public void accept0(OdpsASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, this.selectList); + acceptChild(visitor, this.from); + acceptChild(visitor, this.where); + acceptChild(visitor, this.groupBy); + acceptChild(visitor, this.orderBy); + acceptChild(visitor, this.limit); + acceptChild(visitor, this.into); + } + + visitor.endVisit(this); + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsLexer.java index 627abf08a7..0de4ee60af 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsLexer.java @@ -42,6 +42,7 @@ public class OdpsLexer extends Lexer { map.put("PARTITIONED", Token.PARTITIONED); map.put("OVERWRITE", Token.OVERWRITE); map.put("OVER", Token.OVER); + map.put("LIMIT", Token.LIMIT); DEFAULT_ODPS_KEYWORDS = new Keywords(map); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsSelectParser.java index f897d1dcfc..f4299db585 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsSelectParser.java @@ -16,20 +16,72 @@ package com.alibaba.druid.sql.dialect.odps.parser; import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLSetQuantifier; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.statement.SQLSelectItem; +import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsSelectQueryBlock; import com.alibaba.druid.sql.dialect.odps.ast.OdpsUDTFSQLSelectItem; import com.alibaba.druid.sql.parser.SQLExprParser; import com.alibaba.druid.sql.parser.SQLSelectParser; import com.alibaba.druid.sql.parser.Token; - public class OdpsSelectParser extends SQLSelectParser { + public OdpsSelectParser(SQLExprParser exprParser){ super(exprParser.getLexer()); this.exprParser = exprParser; } - + + @Override + public SQLSelectQuery query() { + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + + SQLSelectQuery select = query(); + accept(Token.RPAREN); + + return queryRest(select); + } + + accept(Token.SELECT); + + if (lexer.token() == Token.COMMENT) { + lexer.nextToken(); + } + + OdpsSelectQueryBlock queryBlock = new OdpsSelectQueryBlock(); + + if (lexer.token() == Token.DISTINCT) { + queryBlock.setDistionOption(SQLSetQuantifier.DISTINCT); + lexer.nextToken(); + } else if (lexer.token() == Token.UNIQUE) { + queryBlock.setDistionOption(SQLSetQuantifier.UNIQUE); + lexer.nextToken(); + } else if (lexer.token() == Token.ALL) { + queryBlock.setDistionOption(SQLSetQuantifier.ALL); + lexer.nextToken(); + } + + parseSelectList(queryBlock); + + parseFrom(queryBlock); + + parseWhere(queryBlock); + + parseGroupBy(queryBlock); + + queryBlock.setOrderBy(this.exprParser.parseOrderBy()); + + if (lexer.token() == Token.LIMIT) { + lexer.nextToken(); + queryBlock.setLimit(this.expr()); + } + + return queryRest(queryBlock); + } + + @Override protected SQLSelectItem parseSelectItem() { SQLExpr expr; if (lexer.token() == Token.IDENTIFIER) { @@ -43,36 +95,36 @@ public class OdpsSelectParser extends SQLSelectParser { } else { expr = expr(); } - + if (lexer.token() == Token.AS) { lexer.nextToken(); - + if (lexer.token() == Token.LPAREN) { lexer.nextToken(); - + OdpsUDTFSQLSelectItem selectItem = new OdpsUDTFSQLSelectItem(); - + selectItem.setExpr(expr); - + for (;;) { String alias = lexer.stringVal(); lexer.nextToken(); - + selectItem.getAliasList().add(alias); - + if (lexer.token() == Token.COMMA) { lexer.nextToken(); continue; } break; } - + accept(Token.RPAREN); - + return selectItem; } } - + final String alias = as(); return new SQLSelectItem(expr, alias); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsStatementParser.java index 848305a7fb..b477de0075 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsStatementParser.java @@ -24,6 +24,7 @@ import com.alibaba.druid.sql.ast.statement.SQLAssignItem; import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; 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.SQLSubqueryTableSource; import com.alibaba.druid.sql.dialect.odps.ast.OdpsInsert; @@ -37,6 +38,7 @@ import com.alibaba.druid.sql.parser.SQLExprParser; import com.alibaba.druid.sql.parser.SQLSelectParser; import com.alibaba.druid.sql.parser.SQLStatementParser; import com.alibaba.druid.sql.parser.Token; +import com.alibaba.druid.util.JdbcConstants; public class OdpsStatementParser extends SQLStatementParser { @@ -48,6 +50,11 @@ public class OdpsStatementParser extends SQLStatementParser { super(exprParser); } + public SQLSelectStatement parseSelect() { + OdpsSelectParser selectParser = new OdpsSelectParser(this.exprParser); + return new SQLSelectStatement(selectParser.select(), JdbcConstants.ODPS); + } + public SQLCreateTableStatement parseCreateTable() { SQLCreateTableParser parser = new OdpsCreateTableParser(this.exprParser); return parser.parseCrateTable(); @@ -179,7 +186,7 @@ public class OdpsStatementParser extends SQLStatementParser { accept(Token.TO); if (lexer.token() == Token.USER) { lexer.nextToken(); - + SQLName name = this.exprParser.name(); stmt.setUser(name); return stmt; @@ -187,13 +194,13 @@ public class OdpsStatementParser extends SQLStatementParser { accept(Token.TABLE); SQLExpr expr = this.exprParser.name(); stmt.setTable(new SQLExprTableSource(expr)); - + if (lexer.token() == Token.LPAREN) { lexer.nextToken(); this.exprParser.names(stmt.getColumns(), stmt); accept(Token.RPAREN); } - + return stmt; } else { SQLSetStatement stmt = new SQLSetStatement(getDbType()); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsASTVisitor.java index dc75e01c70..9297cda28f 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsASTVisitor.java @@ -18,6 +18,7 @@ package com.alibaba.druid.sql.dialect.odps.visitor; import com.alibaba.druid.sql.dialect.odps.ast.OdpsCreateTableStatement; import com.alibaba.druid.sql.dialect.odps.ast.OdpsInsert; import com.alibaba.druid.sql.dialect.odps.ast.OdpsInsertStatement; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsSelectQueryBlock; import com.alibaba.druid.sql.dialect.odps.ast.OdpsSetLabelStatement; import com.alibaba.druid.sql.dialect.odps.ast.OdpsShowPartitionsStmt; import com.alibaba.druid.sql.dialect.odps.ast.OdpsShowStatisticStmt; @@ -53,4 +54,8 @@ public interface OdpsASTVisitor extends SQLASTVisitor { void endVisit(OdpsSetLabelStatement x); boolean visit(OdpsSetLabelStatement x); + + void endVisit(OdpsSelectQueryBlock x); + + boolean visit(OdpsSelectQueryBlock x); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsOutputVisitor.java index 4fa1f9ff22..86a6244d5f 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsOutputVisitor.java @@ -17,6 +17,8 @@ package com.alibaba.druid.sql.dialect.odps.visitor; import java.util.List; +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.expr.SQLCaseExpr; import com.alibaba.druid.sql.ast.statement.SQLAssignItem; @@ -29,6 +31,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; import com.alibaba.druid.sql.dialect.odps.ast.OdpsCreateTableStatement; import com.alibaba.druid.sql.dialect.odps.ast.OdpsInsert; import com.alibaba.druid.sql.dialect.odps.ast.OdpsInsertStatement; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsSelectQueryBlock; import com.alibaba.druid.sql.dialect.odps.ast.OdpsSetLabelStatement; import com.alibaba.druid.sql.dialect.odps.ast.OdpsShowPartitionsStmt; import com.alibaba.druid.sql.dialect.odps.ast.OdpsShowStatisticStmt; @@ -92,7 +95,7 @@ public class OdpsOutputVisitor extends SQLASTOutputVisitor implements OdpsASTVis } print(")"); } - + if (x.getLifecycle() != null) { println(); print("LIFECYCLE "); @@ -132,7 +135,6 @@ public class OdpsOutputVisitor extends SQLASTOutputVisitor implements OdpsASTVis } for (OdpsInsert insert : x.getItems()) { - println(); println(); insert.accept(this); } @@ -149,7 +151,7 @@ public class OdpsOutputVisitor extends SQLASTOutputVisitor implements OdpsASTVis if (x.isOverwrite()) { print("INSERT OVERWRITE TABLE "); } else { - print("INSERT INTO OVERWRITE "); + print("INSERT INTO TABLE "); } x.getTableSource().accept(this); @@ -251,7 +253,7 @@ public class OdpsOutputVisitor extends SQLASTOutputVisitor implements OdpsASTVis return false; } - + @Override public boolean visit(SQLJoinTableSource x) { x.getLeft().accept(this); @@ -288,7 +290,7 @@ public class OdpsOutputVisitor extends SQLASTOutputVisitor implements OdpsASTVis @Override public void endVisit(OdpsUDTFSQLSelectItem x) { - + } @Override @@ -296,14 +298,14 @@ public class OdpsOutputVisitor extends SQLASTOutputVisitor implements OdpsASTVis x.getExpr().accept(this); print(" AS ("); - + for (int i = 0; i < x.getAliasList().size(); ++i) { if (i != 0) { print(", "); } print(x.getAliasList().get(i)); } - + print(")"); return false; @@ -311,7 +313,7 @@ public class OdpsOutputVisitor extends SQLASTOutputVisitor implements OdpsASTVis @Override public void endVisit(OdpsShowPartitionsStmt x) { - + } @Override @@ -320,12 +322,12 @@ public class OdpsOutputVisitor extends SQLASTOutputVisitor implements OdpsASTVis x.getTableSource().accept(this); return false; } - + @Override public void endVisit(OdpsShowStatisticStmt x) { - + } - + @Override public boolean visit(OdpsShowStatisticStmt x) { print("SHOW STATISTIC "); @@ -335,7 +337,7 @@ public class OdpsOutputVisitor extends SQLASTOutputVisitor implements OdpsASTVis @Override public void endVisit(OdpsSetLabelStatement x) { - + } @Override @@ -343,7 +345,7 @@ public class OdpsOutputVisitor extends SQLASTOutputVisitor implements OdpsASTVis print("SET LABEL "); print(x.getLabel()); print(" TO "); - + if (x.getUser() != null) { print("USER "); x.getUser().accept(this); @@ -356,7 +358,75 @@ public class OdpsOutputVisitor extends SQLASTOutputVisitor implements OdpsASTVis print(")"); } } + + return false; + } + + @Override + public void endVisit(OdpsSelectQueryBlock x) { + + } + + @Override + public boolean visit(OdpsSelectQueryBlock x) { + print("SELECT "); + + if (SQLSetQuantifier.ALL == x.getDistionOption()) { + print("ALL "); + } else if (SQLSetQuantifier.DISTINCT == x.getDistionOption()) { + print("DISTINCT "); + } else if (SQLSetQuantifier.UNIQUE == x.getDistionOption()) { + print("UNIQUE "); + } + + printSelectList(x.getSelectList()); + + if (x.getFrom() != null) { + println(); + print("FROM "); + x.getFrom().accept(this); + } + + if (x.getWhere() != null) { + println(); + print("WHERE "); + x.getWhere().setParent(x); + x.getWhere().accept(this); + } + + if (x.getGroupBy() != null) { + println(); + x.getGroupBy().accept(this); + } + + if (x.getOrderBy() != null) { + println(); + x.getOrderBy().accept(this); + } + if (x.getLimit() != null) { + println(); + print("LIMIT "); + x.getLimit().accept(this); + } + + return false; + } + + public boolean visit(SQLOrderBy x) { + int itemSize = x.getItems().size(); + if (itemSize > 0) { + print("ORDER BY "); + incrementIndent(); + for (int i = 0; i < itemSize; ++i) { + if (i != 0) { + println(", "); + } + x.getItems().get(i).accept(this); + } + decrementIndent(); + } + return false; } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsSchemaStatVisitor.java index d90dd9ead1..9d85e02264 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsSchemaStatVisitor.java @@ -16,16 +16,17 @@ package com.alibaba.druid.sql.dialect.odps.visitor; import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.odps.ast.OdpsCreateTableStatement; import com.alibaba.druid.sql.dialect.odps.ast.OdpsInsert; import com.alibaba.druid.sql.dialect.odps.ast.OdpsInsertStatement; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsSelectQueryBlock; import com.alibaba.druid.sql.dialect.odps.ast.OdpsSetLabelStatement; import com.alibaba.druid.sql.dialect.odps.ast.OdpsShowPartitionsStmt; import com.alibaba.druid.sql.dialect.odps.ast.OdpsShowStatisticStmt; import com.alibaba.druid.sql.dialect.odps.ast.OdpsUDTFSQLSelectItem; import com.alibaba.druid.sql.visitor.SchemaStatVisitor; - public class OdpsSchemaStatVisitor extends SchemaStatVisitor implements OdpsASTVisitor { @Override @@ -40,7 +41,7 @@ public class OdpsSchemaStatVisitor extends SchemaStatVisitor implements OdpsASTV @Override public void endVisit(OdpsInsertStatement x) { - + } @Override @@ -50,7 +51,7 @@ public class OdpsSchemaStatVisitor extends SchemaStatVisitor implements OdpsASTV @Override public void endVisit(OdpsInsert x) { - + } @Override @@ -60,7 +61,7 @@ public class OdpsSchemaStatVisitor extends SchemaStatVisitor implements OdpsASTV @Override public void endVisit(OdpsUDTFSQLSelectItem x) { - + } @Override @@ -70,29 +71,29 @@ public class OdpsSchemaStatVisitor extends SchemaStatVisitor implements OdpsASTV @Override public void endVisit(OdpsShowPartitionsStmt x) { - + } @Override public boolean visit(OdpsShowPartitionsStmt x) { return true; } - + @Override public void endVisit(OdpsShowStatisticStmt x) { - + } - + @Override public boolean visit(OdpsShowStatisticStmt x) { return true; } - + @Override public void endVisit(OdpsSetLabelStatement x) { - + } - + @Override public boolean visit(OdpsSetLabelStatement x) { if (x.getTable() != null) { @@ -101,5 +102,14 @@ public class OdpsSchemaStatVisitor extends SchemaStatVisitor implements OdpsASTV return false; } - + @Override + public void endVisit(OdpsSelectQueryBlock x) { + + } + + @Override + public boolean visit(OdpsSelectQueryBlock x) { + return this.visit((SQLSelectQueryBlock) x); + } + } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java b/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java index 3b33e89656..36ec1adf71 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java @@ -20,6 +20,7 @@ import com.alibaba.druid.sql.dialect.db2.parser.DB2StatementParser; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlExprParser; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; import com.alibaba.druid.sql.dialect.odps.parser.OdpsExprParser; +import com.alibaba.druid.sql.dialect.odps.parser.OdpsStatementParser; import com.alibaba.druid.sql.dialect.oracle.parser.OracleExprParser; import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; import com.alibaba.druid.sql.dialect.postgresql.parser.PGExprParser; @@ -58,6 +59,10 @@ public class SQLParserUtils { if (JdbcUtils.DB2.equals(dbType)) { return new DB2StatementParser(sql); } + + if (JdbcUtils.ODPS.equals(dbType)) { + return new OdpsStatementParser(sql); + } return new SQLStatementParser(sql); }