diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java index 5af9100a8a..38f82a6fa1 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java @@ -158,6 +158,8 @@ public class MySqlSelectParser extends SQLSelectParser { parseWhere(queryBlock); + parseHierachical(queryBlock); + parseGroupBy(queryBlock); queryBlock.setOrderBy(this.exprParser.parseOrderBy()); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java index 283bf3271f..16147caca7 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java @@ -279,6 +279,8 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV x.getWhere().accept(this); } + printHierarchical(x); + if (x.getGroupBy() != null) { println(); x.getGroupBy().accept(this); 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 6490d36b54..e07dd29c60 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 @@ -148,7 +148,7 @@ public class OdpsSelectParser extends SQLSelectParser { break; } - String alias = this.as(); + String alias = this.tableAlias(); tableSource.setAlias(alias); accept(Token.LPAREN); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java index d78b91c0eb..9b0a3c432a 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java @@ -563,66 +563,9 @@ public class OracleSelectParser extends SQLSelectParser { } protected String as() { - if (lexer.token() == Token.CONNECT) { - return null; - } - return super.as(); } - private void parseHierachical(OracleSelectQueryBlock queryBlock) { - - - if (lexer.token() == Token.CONNECT) { - lexer.nextToken(); - accept(Token.BY); - - if (lexer.token() == Token.PRIOR) { - lexer.nextToken(); - queryBlock.setPrior(true); - } - - if (identifierEquals("NOCYCLE")) { - queryBlock.setNoCycle(true); - lexer.nextToken(); - - if (lexer.token() == Token.PRIOR) { - lexer.nextToken(); - queryBlock.setPrior(true); - } - } - queryBlock.setConnectBy(this.exprParser.expr()); - } - - if (lexer.token() == Token.START) { - lexer.nextToken(); - accept(Token.WITH); - - queryBlock.setStartWith(this.exprParser.expr()); - } - - if (lexer.token() == Token.CONNECT) { - lexer.nextToken(); - accept(Token.BY); - - if (lexer.token() == Token.PRIOR) { - lexer.nextToken(); - queryBlock.setPrior(true); - } - - if (identifierEquals("NOCYCLE")) { - queryBlock.setNoCycle(true); - lexer.nextToken(); - - if (lexer.token() == Token.PRIOR) { - lexer.nextToken(); - queryBlock.setPrior(true); - } - } - queryBlock.setConnectBy(this.exprParser.expr()); - } - } - @Override public SQLTableSource parseTableSource() { if (lexer.token() == (Token.LPAREN)) { @@ -807,10 +750,10 @@ public class OracleSelectParser extends SQLSelectParser { tableSource.setFlashback(flashback()); } - tableSource.setAlias(as()); + tableSource.setAlias(tableAlias()); } else if ((tableSource.getAlias() == null) || (tableSource.getAlias().length() == 0)) { if (lexer.token() != Token.LEFT && lexer.token() != Token.RIGHT && lexer.token() != Token.FULL) { - tableSource.setAlias(as()); + tableSource.setAlias(tableAlias()); } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java index c8b74fd153..b9ffec7633 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java @@ -1273,7 +1273,7 @@ public class OracleStatementParser extends SQLStatementParser { deleteStatement.setTableName(tableName); } - deleteStatement.setAlias(as()); + deleteStatement.setAlias(tableAlias()); } if (lexer.token() == (Token.WHERE)) { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleUpdateParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleUpdateParser.java index 585e63f7c6..2db8edbbfe 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleUpdateParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleUpdateParser.java @@ -49,7 +49,7 @@ public class OracleUpdateParser extends SQLStatementParser { update.setTableSource(tableSource); if ((update.getAlias() == null) || (update.getAlias().length() == 0)) { - update.setAlias(as()); + update.setAlias(tableAlias()); } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java index 64cce5069b..c2ea90b3f4 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java @@ -527,27 +527,7 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS x.getWhere().accept(this); } - SQLExpr startWith = x.getStartWith(), connectBy = x.getConnectBy(); - if (startWith != null || connectBy != null){ - println(); - if (x.getStartWith() != null) { - print0(ucase ? "START WITH " : "start with "); - x.getStartWith().accept(this); - println(); - } - - print0(ucase ? "CONNECT BY " : "connect by "); - - if (x.isNoCycle()) { - print0(ucase ? "NOCYCLE " : "nocycle "); - } - - if (x.isPrior()) { - print0(ucase ? "PRIOR " : "prior "); - } - - x.getConnectBy().accept(this); - } + printHierarchical(x); if (x.getGroupBy() != null) { println(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java index 196e523a05..a2cb7a9705 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java @@ -256,7 +256,7 @@ public class SQLServerStatementParser extends SQLStatementParser { insertStatement.setTableName(tableName); if (lexer.token() == Token.LITERAL_ALIAS) { - insertStatement.setAlias(as()); + insertStatement.setAlias(tableAlias()); } parseInsert0_hinits(insertStatement); diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLParser.java index 0511a703cd..c8b098a0e7 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLParser.java @@ -60,10 +60,28 @@ public class SQLParser { } } + protected String tableAlias() { + if (lexer.token() == Token.CONNECT || lexer.token() == Token.START) { + return null; + } + + if (identifierEquals("START") || identifierEquals("CONNECT")) { + return null; + } + + return this.as(); + } + protected String as() { String alias = null; - if (lexer.token() == Token.AS) { + final Token token = lexer.token(); + + if (token == Token.COMMA) { + return null; + } + + if (token == Token.AS) { lexer.nextToken(); alias = alias(); diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java index 86a690fb2b..2876891355 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java @@ -448,7 +448,7 @@ public class SQLSelectParser extends SQLParser { if ((tableSource.getAlias() == null) || (tableSource.getAlias().length() == 0)) { if (lexer.token() != Token.LEFT && lexer.token() != Token.RIGHT && lexer.token() != Token.FULL && !identifierEquals("STRAIGHT_JOIN") && !identifierEquals("CROSS") && lexer.token != Token.OUTER) { - String alias = as(); + String alias = tableAlias(); if (alias != null) { tableSource.setAlias(alias); return parseTableSourceRest(tableSource); @@ -589,4 +589,55 @@ public class SQLSelectParser extends SQLParser { } } } + + protected void parseHierachical(SQLSelectQueryBlock queryBlock) { + if (lexer.token() == Token.CONNECT || identifierEquals("CONNECT")) { + lexer.nextToken(); + accept(Token.BY); + + if (lexer.token() == Token.PRIOR || identifierEquals("PRIOR")) { + lexer.nextToken(); + queryBlock.setPrior(true); + } + + if (identifierEquals("NOCYCLE")) { + queryBlock.setNoCycle(true); + lexer.nextToken(); + + if (lexer.token() == Token.PRIOR) { + lexer.nextToken(); + queryBlock.setPrior(true); + } + } + queryBlock.setConnectBy(this.exprParser.expr()); + } + + if (lexer.token() == Token.START || identifierEquals("START")) { + lexer.nextToken(); + accept(Token.WITH); + + queryBlock.setStartWith(this.exprParser.expr()); + } + + if (lexer.token() == Token.CONNECT || identifierEquals("CONNECT")) { + lexer.nextToken(); + accept(Token.BY); + + if (lexer.token() == Token.PRIOR || identifierEquals("PRIOR")) { + lexer.nextToken(); + queryBlock.setPrior(true); + } + + if (identifierEquals("NOCYCLE")) { + queryBlock.setNoCycle(true); + lexer.nextToken(); + + if (lexer.token() == Token.PRIOR || identifierEquals("PRIOR")) { + lexer.nextToken(); + queryBlock.setPrior(true); + } + } + queryBlock.setConnectBy(this.exprParser.expr()); + } + } } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java index 181519d39b..a17a961e63 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -1511,7 +1511,7 @@ public class SQLStatementParser extends SQLParser { insertStatement.setTableName(tableName); if (lexer.token() == Token.LITERAL_ALIAS) { - insertStatement.setAlias(as()); + insertStatement.setAlias(tableAlias()); } parseInsert0_hinits(insertStatement); @@ -2247,7 +2247,7 @@ public class SQLStatementParser extends SQLParser { stmt.setInto(exprParser.name()); } - stmt.setAlias(as()); + stmt.setAlias(tableAlias()); accept(Token.USING); 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 7f8c86b3f8..f477c4085e 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -33,6 +33,7 @@ import com.alibaba.druid.sql.ast.statement.SQLInsertStatement.ValuesClause; import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource.JoinType; import com.alibaba.druid.sql.ast.statement.SQLMergeStatement.MergeInsertClause; import com.alibaba.druid.sql.ast.statement.SQLMergeStatement.MergeUpdateClause; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock; import com.alibaba.druid.util.JdbcConstants; public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements ParameterizedVisitor, PrintableVisitor { @@ -3955,4 +3956,28 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet } return false; } + + protected void printHierarchical(SQLSelectQueryBlock x) { + SQLExpr startWith = x.getStartWith(), connectBy = x.getConnectBy(); + if (startWith != null || connectBy != null){ + println(); + if (x.getStartWith() != null) { + print0(ucase ? "START WITH " : "start with "); + x.getStartWith().accept(this); + println(); + } + + print0(ucase ? "CONNECT BY " : "connect by "); + + if (x.isNoCycle()) { + print0(ucase ? "NOCYCLE " : "nocycle "); + } + + if (x.isPrior()) { + print0(ucase ? "PRIOR " : "prior "); + } + + x.getConnectBy().accept(this); + } + } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oceanbase/OceanbaseSelectTest.java b/src/test/java/com/alibaba/druid/bvt/sql/oceanbase/OceanbaseSelectTest.java new file mode 100644 index 0000000000..12c21a73ac --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oceanbase/OceanbaseSelectTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 1999-2017 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.oceanbase; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.visitor.SchemaStatVisitor; +import com.alibaba.druid.util.JdbcConstants; + +import java.util.List; + +public class OceanbaseSelectTest extends MysqlTest { + protected final String dbType = JdbcConstants.MYSQL; + + public void test_0() throws Exception { + String sql = "SELECT EmpID, EmpName, MgrId, Level FROM emp START WITH MgrId IS NULL CONNECT BY PRIOR EmpId = MgrId"; // + + List stmtList = SQLUtils.parseStatements(sql, dbType); + SQLStatement stmt = stmtList.get(0); + + { + String result = SQLUtils.toSQLString(stmt, dbType); + assertEquals("SELECT EmpID, EmpName, MgrId, Level\n" + + "FROM emp\n" + + "START WITH MgrId IS NULL\n" + + "CONNECT BY PRIOR EmpId = MgrId", result); + } + { + String result = SQLUtils.toSQLString(stmt, dbType, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION); + assertEquals("select EmpID, EmpName, MgrId, Level\n" + + "from emp\n" + + "start with MgrId is null\n" + + "connect by prior EmpId = MgrId", result); + } + + assertEquals(1, stmtList.size()); + + SchemaStatVisitor visitor = SQLUtils.createSchemaStatVisitor(dbType); + 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()); + + assertEquals(1, visitor.getTables().size()); + assertEquals(4, visitor.getColumns().size()); + assertEquals(0, visitor.getConditions().size()); + + // assertTrue(visitor.getTables().containsKey(new TableStat.Name("t_basic_store"))); + + } +}