diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectQueryBlock.java index 88682d94d8..f419b7c378 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectQueryBlock.java @@ -37,6 +37,7 @@ public class SQLSelectQueryBlock extends SQLObjectImpl implements SQLSelectQuery protected boolean forUpdate = false; protected boolean noWait = false; protected SQLExpr waitTime; + protected SQLExpr first; public SQLSelectQueryBlock(){ @@ -142,9 +143,24 @@ public class SQLSelectQueryBlock extends SQLObjectImpl implements SQLSelectQuery } public void setWaitTime(SQLExpr waitTime) { + if (waitTime != null) { + waitTime.setParent(this); + } this.waitTime = waitTime; } + + public SQLExpr getFirst() { + return first; + } + + public void setFirst(SQLExpr first) { + if (first != null) { + first.setParent(this); + } + this.first = first; + } + @Override protected void accept0(SQLASTVisitor visitor) { if (visitor.visit(this)) { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/stmt/DB2SelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/stmt/DB2SelectQueryBlock.java index 12caa56c1e..77c0e4fd2a 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/stmt/DB2SelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/stmt/DB2SelectQueryBlock.java @@ -23,7 +23,7 @@ import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class DB2SelectQueryBlock extends SQLSelectQueryBlock implements DB2Object { - private SQLExpr first; + private Isolation isolation; @@ -31,14 +31,6 @@ public class DB2SelectQueryBlock extends SQLSelectQueryBlock implements DB2Objec private SQLExpr optimizeFor; - public SQLExpr getFirst() { - return first; - } - - public void setFirst(SQLExpr first) { - this.first = first; - } - @Override protected void accept0(SQLASTVisitor visitor) { if (visitor instanceof DB2ASTVisitor) { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2OutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2OutputVisitor.java index fc58862730..1b435b6d99 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2OutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2OutputVisitor.java @@ -38,27 +38,6 @@ public class DB2OutputVisitor extends SQLASTOutputVisitor implements DB2ASTVisit public boolean visit(DB2SelectQueryBlock x) { this.visit((SQLSelectQueryBlock) x); - if (x.getFirst() != null) { - - //order by 语句必须在FETCH FIRST ROWS ONLY之前 - SQLObject parent= x.getParent(); - if(parent instanceof SQLSelect) - { - SQLOrderBy orderBy= ((SQLSelect) parent).getOrderBy(); - if (orderBy!=null&&orderBy.getItems().size() > 0) { - println(); - print0(ucase ? "ORDER BY " : "order by "); - printAndAccept(orderBy.getItems(), ", "); - ((SQLSelect) parent).setOrderBy(null); - } - } - println(); - print0(ucase ? "FETCH FIRST " : "fetch first "); - x.getFirst().accept(this); - print0(ucase ? " ROWS ONLY" : " rows only"); - - - } if (x.isForReadOnly()) { println(); print0(ucase ? "FOR READ ONLY" : "for read only"); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java index 48bb07b9e0..b5e54d3063 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java @@ -109,6 +109,8 @@ public class OracleLexer extends Lexer { map.put("PURGE", Token.PURGE); map.put("INITIALLY", Token.INITIALLY); + map.put("FETCH", Token.FETCH); + DEFAULT_ORACLE_KEYWORDS = new Keywords(map); } 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 c87ebdca0f..a9bec4e02e 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 @@ -276,6 +276,8 @@ public class OracleSelectParser extends SQLSelectParser { parseModelClause(queryBlock); + parseFetchClause(queryBlock); + return queryRest(queryBlock); } 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 b44d4b8792..299f025971 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 @@ -17,12 +17,7 @@ package com.alibaba.druid.sql.dialect.oracle.visitor; import java.util.List; -import com.alibaba.druid.sql.ast.SQLExpr; -import com.alibaba.druid.sql.ast.SQLHint; -import com.alibaba.druid.sql.ast.SQLObject; -import com.alibaba.druid.sql.ast.SQLParameter; -import com.alibaba.druid.sql.ast.SQLSetQuantifier; -import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.*; import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; @@ -572,6 +567,8 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS x.getModelClause().accept(this); } + printFetchFirst(x); + return false; } 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 44352da2ef..efb0798f7b 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java @@ -540,4 +540,25 @@ public class SQLSelectParser extends SQLParser { } } + public void parseFetchClause(SQLSelectQueryBlock queryBlock) { + if (lexer.token() == Token.FETCH) { + lexer.nextToken(); + if (lexer.token() == Token.FIRST) { + lexer.nextToken(); + } else { + acceptIdentifier("FIRST"); + } + SQLExpr first = this.exprParser.primary(); + queryBlock.setFirst(first); + if (identifierEquals("ROW") || identifierEquals("ROWS")) { + lexer.nextToken(); + } + + if (lexer.token() == Token.ONLY) { + lexer.nextToken(); + } else { + acceptIdentifier("ONLY"); + } + } + } } 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 e7fc19485c..dca94b964e 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -787,9 +787,31 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet x.getOrderBy().accept(this); } + printFetchFirst(x); + return false; } + protected void printFetchFirst(SQLSelectQueryBlock x) { + if (x.getFirst() != null) { + //order by 语句必须在FETCH FIRST ROWS ONLY之前 + SQLObject parent= x.getParent(); + if(parent instanceof SQLSelect) { + SQLOrderBy orderBy = ((SQLSelect) parent).getOrderBy(); + if (orderBy!=null&&orderBy.getItems().size() > 0) { + println(); + print0(ucase ? "ORDER BY " : "order by "); + printAndAccept(orderBy.getItems(), ", "); + ((SQLSelect) parent).setOrderBy(null); + } + } + println(); + print0(ucase ? "FETCH FIRST " : "fetch first "); + x.getFirst().accept(this); + print0(ucase ? " ROWS ONLY" : " rows only"); + } + } + public boolean visit(SQLSelectItem x) { if (x.isConnectByRoot()) { print0(ucase ? "CONNECT_BY_ROOT " : "connect_by_root "); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/select/OracleSelectTest58.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/select/OracleSelectTest58.java new file mode 100644 index 0000000000..c60a0a465c --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/select/OracleSelectTest58.java @@ -0,0 +1,72 @@ +/* + * 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.oracle.select; + +import com.alibaba.druid.sql.OracleTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleSchemaStatVisitor; +import org.junit.Assert; + +import java.util.List; + +public class OracleSelectTest58 extends OracleTest { + + public void test_0() throws Exception { + String sql = // + "SELECT * FROM T FETCH FIRST 10 ROWS ONLY"; // + + OracleStatementParser parser = new OracleStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + OracleSchemaStatVisitor visitor = new OracleSchemaStatVisitor(); + stmt.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("relationships : " + visitor.getRelationships()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(1, visitor.getTables().size()); + + Assert.assertEquals(1, visitor.getColumns().size()); + + { + String text = SQLUtils.toOracleString(stmt); + + Assert.assertEquals("SELECT *\n" + + "FROM T\n" + + "FETCH FIRST 10 ROWS ONLY", text); + } + + { + String text = SQLUtils.toOracleString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION); + + Assert.assertEquals("select *\n" + + "from T\n" + + "fetch first 10 rows only", text); + } + // Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("acduser.vw_acd_info", "xzqh"))); + + // Assert.assertTrue(visitor.getOrderByColumns().contains(new TableStat.Column("employees", "last_name"))); + } +}