diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsLateralViewTableSource.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsLateralViewTableSource.java new file mode 100644 index 0000000000..ccbe2bd4c5 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsLateralViewTableSource.java @@ -0,0 +1,59 @@ +package com.alibaba.druid.sql.dialect.odps.ast; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; +import com.alibaba.druid.sql.ast.statement.SQLTableSource; +import com.alibaba.druid.sql.ast.statement.SQLTableSourceImpl; +import com.alibaba.druid.sql.dialect.odps.visitor.OdpsASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class OdpsLateralViewTableSource extends SQLTableSourceImpl { + + private SQLTableSource tableSource; + + private SQLMethodInvokeExpr method; + + private List columns = new ArrayList(2); + + @Override + protected void accept0(SQLASTVisitor visitor) { + accept0((OdpsASTVisitor) visitor); + } + + protected void accept0(OdpsASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, tableSource); + acceptChild(visitor, method); + acceptChild(visitor, columns); + } + visitor.endVisit(this); + } + + public SQLTableSource getTableSource() { + return tableSource; + } + + public void setTableSource(SQLTableSource tableSource) { + this.tableSource = tableSource; + } + + public SQLMethodInvokeExpr getMethod() { + return method; + } + + public void setMethod(SQLMethodInvokeExpr method) { + this.method = method; + } + + public List getColumns() { + return columns; + } + + public void setColumns(List columns) { + this.columns = columns; + } + +} 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 59a0c0e1ab..62442cf85f 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 @@ -18,8 +18,11 @@ package com.alibaba.druid.sql.dialect.odps.parser; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLOrderingSpecification; import com.alibaba.druid.sql.ast.SQLSetQuantifier; +import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem; import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; +import com.alibaba.druid.sql.ast.statement.SQLTableSource; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsLateralViewTableSource; import com.alibaba.druid.sql.dialect.odps.ast.OdpsSelectQueryBlock; import com.alibaba.druid.sql.parser.SQLExprParser; import com.alibaba.druid.sql.parser.SQLSelectParser; @@ -121,5 +124,39 @@ public class OdpsSelectParser extends SQLSelectParser { return queryRest(queryBlock); } + + protected SQLTableSource parseTableSourceRest(SQLTableSource tableSource) { + tableSource = super.parseTableSourceRest(tableSource); + + if ("LATERAL".equalsIgnoreCase(tableSource.getAlias()) && lexer.token() == Token.VIEW) { + return parseLateralView(tableSource); + } + + if (identifierEquals("LATERAL")) { + lexer.nextToken(); + return parseLateralView(tableSource); + } + + return tableSource; + } + + protected SQLTableSource parseLateralView(SQLTableSource tableSource) { + accept(Token.VIEW); + tableSource.setAlias(null); + OdpsLateralViewTableSource lateralViewTabSrc = new OdpsLateralViewTableSource(); + lateralViewTabSrc.setTableSource(tableSource); + + SQLMethodInvokeExpr udtf = (SQLMethodInvokeExpr) this.exprParser.expr(); + lateralViewTabSrc.setMethod(udtf); + + String alias = as(); + lateralViewTabSrc.setAlias(alias); + + accept(Token.AS); + + this.exprParser.names(lateralViewTabSrc.getColumns()); + + return parseTableSourceRest(lateralViewTabSrc); + } } 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 75d5d2fc49..e054fdbefb 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 @@ -22,6 +22,7 @@ import com.alibaba.druid.sql.dialect.odps.ast.OdpsDescStmt; import com.alibaba.druid.sql.dialect.odps.ast.OdpsGrantStmt; 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.OdpsLateralViewTableSource; import com.alibaba.druid.sql.dialect.odps.ast.OdpsListStmt; import com.alibaba.druid.sql.dialect.odps.ast.OdpsReadStatement; import com.alibaba.druid.sql.dialect.odps.ast.OdpsRemoveStatisticStatement; @@ -123,4 +124,8 @@ public interface OdpsASTVisitor extends SQLASTVisitor { void endVisit(OdpsDescStmt x); boolean visit(OdpsDescStmt x); + + void endVisit(OdpsLateralViewTableSource x); + + boolean visit(OdpsLateralViewTableSource x); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsASTVisitorAdapter.java index 9d36b6f1e7..01301b60d6 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsASTVisitorAdapter.java @@ -22,6 +22,7 @@ import com.alibaba.druid.sql.dialect.odps.ast.OdpsDescStmt; import com.alibaba.druid.sql.dialect.odps.ast.OdpsGrantStmt; 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.OdpsLateralViewTableSource; import com.alibaba.druid.sql.dialect.odps.ast.OdpsListStmt; import com.alibaba.druid.sql.dialect.odps.ast.OdpsReadStatement; import com.alibaba.druid.sql.dialect.odps.ast.OdpsRemoveStatisticStatement; @@ -256,5 +257,15 @@ public class OdpsASTVisitorAdapter extends SQLASTVisitorAdapter implements OdpsA public boolean visit(OdpsDescStmt x) { return true; } + + @Override + public void endVisit(OdpsLateralViewTableSource x) { + + } + + @Override + public boolean visit(OdpsLateralViewTableSource x) { + return true; + } } 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 96134f46dc..82a3f55760 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 @@ -41,6 +41,7 @@ import com.alibaba.druid.sql.dialect.odps.ast.OdpsDescStmt; import com.alibaba.druid.sql.dialect.odps.ast.OdpsGrantStmt; 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.OdpsLateralViewTableSource; import com.alibaba.druid.sql.dialect.odps.ast.OdpsListStmt; import com.alibaba.druid.sql.dialect.odps.ast.OdpsReadStatement; import com.alibaba.druid.sql.dialect.odps.ast.OdpsRemoveStatisticStatement; @@ -847,4 +848,24 @@ public class OdpsOutputVisitor extends SQLASTOutputVisitor implements OdpsASTVis } return false; } + + @Override + public void endVisit(OdpsLateralViewTableSource x) { + + } + + @Override + public boolean visit(OdpsLateralViewTableSource x) { + x.getTableSource().accept(this); + incrementIndent(); + println(); + print0(ucase ? "LATERAL VIEW " : "lateral view "); + x.getMethod().accept(this); + print(' '); + print0(x.getAlias()); + print0(ucase ? " AS " : " as "); + printAndAccept(x.getColumns(), ", "); + 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 99ddaed85c..96fc53f955 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 @@ -25,6 +25,7 @@ import com.alibaba.druid.sql.dialect.odps.ast.OdpsDescStmt; import com.alibaba.druid.sql.dialect.odps.ast.OdpsGrantStmt; 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.OdpsLateralViewTableSource; import com.alibaba.druid.sql.dialect.odps.ast.OdpsListStmt; import com.alibaba.druid.sql.dialect.odps.ast.OdpsReadStatement; import com.alibaba.druid.sql.dialect.odps.ast.OdpsRemoveStatisticStatement; @@ -261,4 +262,14 @@ public class OdpsSchemaStatVisitor extends SchemaStatVisitor implements OdpsASTV public boolean visit(OdpsDescStmt x) { return true; } + + @Override + public void endVisit(OdpsLateralViewTableSource x) { + + } + + @Override + public boolean visit(OdpsLateralViewTableSource x) { + return true; + } } 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 0bdec172e0..087654f033 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java @@ -389,10 +389,9 @@ public class SQLExprParser extends SQLParser { SQLExpr notTarget = expr(); accept(Token.RPAREN); - notTarget = exprRest(notTarget); - + notTarget = relationalRest(notTarget); sqlExpr = new SQLNotExpr(notTarget); - + return primaryRest(sqlExpr); } else { SQLExpr restExpr = relational(); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_24.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_24.java new file mode 100644 index 0000000000..01f22c9024 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_24.java @@ -0,0 +1,65 @@ +/* + * Copyright 1999-2101 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.mysql; + +import java.util.List; + +import org.junit.Assert; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor; + +public class MySqlSelectTest_24 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "select * from company where id=1 and not (name='e') and addr='a'"; + + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); + stmt.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(4, visitor.getColumns().size()); + Assert.assertEquals(3, visitor.getConditions().size()); + Assert.assertEquals(0, visitor.getOrderByColumns().size()); + + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("SELECT *" + + "\nFROM company" + + "\nWHERE id = 1" + + "\n\tAND NOT name = 'e'" + + "\n\tAND addr = 'a'", // + output); + } + + + +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsFormatCommentTest28.java b/src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsFormatCommentTest28.java new file mode 100644 index 0000000000..3f40047d64 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsFormatCommentTest28.java @@ -0,0 +1,17 @@ +package com.alibaba.druid.bvt.sql.odps; + +import org.junit.Assert; + +import com.alibaba.druid.sql.SQLUtils; + +import junit.framework.TestCase; + +public class OdpsFormatCommentTest28 extends TestCase { + public void test_drop_function() throws Exception { + String sql = "SELECT pageid, adid FROM pageAds LATERAL VIEW explode(adid_list) adTable AS adid;"; + Assert.assertEquals("SELECT pageid" + + "\n\t, adid" + + "\nFROM pageAds" + + "\n\tLATERAL VIEW EXPLODE(adid_list) adTable AS adid;", SQLUtils.formatOdps(sql)); + } +}