diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/FlashbackQueryClause.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/FlashbackQueryClause.java index 3c19a4a939..97bd73f214 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/FlashbackQueryClause.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/FlashbackQueryClause.java @@ -6,7 +6,9 @@ import com.alibaba.druid.sql.dialect.oracle.ast.visitor.OracleASTVisitor; public abstract class FlashbackQueryClause extends OracleSQLObject { - private Type type; + private static final long serialVersionUID = 1L; + + private Type type; public Type getType() { return type; @@ -21,7 +23,8 @@ public abstract class FlashbackQueryClause extends OracleSQLObject { } public static class VersionsFlashbackQueryClause extends FlashbackQueryClause { - + private static final long serialVersionUID = 1L; + private SQLExpr begin; private SQLExpr end; @@ -52,7 +55,8 @@ public abstract class FlashbackQueryClause extends OracleSQLObject { } public static class AsOfFlashbackQueryClause extends FlashbackQueryClause { - + private static final long serialVersionUID = 1L; + private SQLExpr expr; public SQLExpr getExpr() { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/visitor/OracleOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/visitor/OracleOutputVisitor.java index 4cb9cf283c..2c8332169d 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/visitor/OracleOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/visitor/OracleOutputVisitor.java @@ -744,6 +744,11 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS decrementIndent(); } + + if (x.getFlashback() != null) { + println(); + x.getFlashback().accept(this); + } printAlias(x.getAlias()); @@ -1545,8 +1550,9 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS public boolean visit(AsOfFlashbackQueryClause x) { print("AS OF "); print(x.getType().name()); - print(" "); + print(" ("); x.getExpr().accept(this); + print(")"); return false; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java index 79c9902779..8470dd9f81 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java @@ -23,6 +23,7 @@ import com.alibaba.druid.sql.dialect.oracle.ast.OracleOrderBy; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAggregateExpr; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalytic; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalyticWindowing; +import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleIntervalExpr; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleIntervalType; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectOrderByItem; import com.alibaba.druid.sql.parser.Lexer; @@ -252,4 +253,24 @@ public class OracleExprParser extends SQLExprParser { return item; } + + protected SQLExpr parseInterval() { + accept(Token.INTERVAL); + + OracleIntervalExpr interval = new OracleIntervalExpr(); + if (lexer.token() != Token.LITERAL_CHARS) { + throw new ParserException("syntax error : " + lexer.token()); + } + interval.setValue(lexer.stringVal()); + lexer.nextToken(); + + if (lexer.token() != Token.IDENTIFIER) { + throw new ParserException("syntax error : " + lexer.token()); + } + OracleIntervalType type = OracleIntervalType.valueOf(lexer.stringVal()); + interval.setType(type); + lexer.nextToken(); + + return interval; + } } 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 772a908b88..81d158c522 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 @@ -16,6 +16,8 @@ package com.alibaba.druid.sql.dialect.oracle.parser; import com.alibaba.druid.sql.ast.SQLSetQuantifier; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause; import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; @@ -23,6 +25,7 @@ import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.AsOfFlashbackQueryClause; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.VersionsFlashbackQueryClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.PartitionExtensionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAggregateExpr; @@ -380,6 +383,34 @@ public class OracleSelectParser extends SQLSelectParser { clause.setType(AsOfFlashbackQueryClause.Type.TIMESTAMP); } clause.setExpr(createExprParser().primary()); + + tableReference.setFlashback(clause); + } else { + throw new SQLParseException("TODO"); + } + } else if (identifierEquals("VERSIONS")) { + lexer.nextToken(); + + if (lexer.token() == Token.BETWEEN) { + lexer.nextToken(); + + VersionsFlashbackQueryClause clause = new VersionsFlashbackQueryClause(); + if (identifierEquals("SCN")) { + clause.setType(AsOfFlashbackQueryClause.Type.SCN); + lexer.nextToken(); + } else { + acceptIdentifier("TIMESTAMP"); + clause.setType(AsOfFlashbackQueryClause.Type.TIMESTAMP); + } + + SQLBinaryOpExpr binaryExpr = (SQLBinaryOpExpr) createExprParser().expr(); + if (binaryExpr.getOperator() != SQLBinaryOperator.BooleanAnd) { + throw new SQLParseException("syntax error : " + binaryExpr.getOperator()); + } + + clause.setBegin(binaryExpr.getLeft()); + clause.setEnd(binaryExpr.getRight()); + tableReference.setFlashback(clause); } else { throw new SQLParseException("TODO"); diff --git a/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSQLParserResourceTest.java b/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSQLParserResourceTest.java index f88f0229fc..83af2d00e0 100644 --- a/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSQLParserResourceTest.java +++ b/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSQLParserResourceTest.java @@ -16,20 +16,22 @@ import com.alibaba.druid.util.JdbcUtils; public class OracleSQLParserResourceTest extends TestCase { public void test_0() throws Exception { -// exec_test("bvt/parser/oracle-0.txt"); -// exec_test("bvt/parser/oracle-1.txt"); -// exec_test("bvt/parser/oracle-2.txt"); -// exec_test("bvt/parser/oracle-3.txt"); -// exec_test("bvt/parser/oracle-4.txt"); -// exec_test("bvt/parser/oracle-5.txt"); -// exec_test("bvt/parser/oracle-6.txt"); // PARTITION -// exec_test("bvt/parser/oracle-7.txt"); -// exec_test("bvt/parser/oracle-8.txt"); + exec_test("bvt/parser/oracle-0.txt"); + exec_test("bvt/parser/oracle-1.txt"); + exec_test("bvt/parser/oracle-2.txt"); + exec_test("bvt/parser/oracle-3.txt"); + exec_test("bvt/parser/oracle-4.txt"); + exec_test("bvt/parser/oracle-5.txt"); + exec_test("bvt/parser/oracle-6.txt"); // PARTITION + exec_test("bvt/parser/oracle-7.txt"); + exec_test("bvt/parser/oracle-8.txt"); exec_test("bvt/parser/oracle-9.txt"); + exec_test("bvt/parser/oracle-10.txt"); } public void exec_test(String resource) throws Exception { + System.out.println(resource); InputStream is = null; is = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource); diff --git a/src/test/resources/bvt/parser/oracle-10.txt b/src/test/resources/bvt/parser/oracle-10.txt new file mode 100644 index 0000000000..cd64841b14 --- /dev/null +++ b/src/test/resources/bvt/parser/oracle-10.txt @@ -0,0 +1,10 @@ +SELECT salary FROM employees + VERSIONS BETWEEN TIMESTAMP + SYSTIMESTAMP - INTERVAL '10' MINUTE AND + SYSTIMESTAMP - INTERVAL '1' MINUTE + WHERE last_name = 'Chung'; +--------------------------- +SELECT salary +FROM employees +VERSIONS BETWEEN TIMESTAMP SYSTIMESTAMP - INTERVAL '10' MINUTE AND SYSTIMESTAMP - INTERVAL '1' MINUTE +WHERE last_name = 'Chung'; \ No newline at end of file diff --git a/src/test/resources/bvt/parser/oracle-5.txt b/src/test/resources/bvt/parser/oracle-5.txt index b51da97529..7db447f92e 100644 --- a/src/test/resources/bvt/parser/oracle-5.txt +++ b/src/test/resources/bvt/parser/oracle-5.txt @@ -9,9 +9,9 @@ FROM FROM employees) b ORDER BY a.department_id; --------------------------- -SELECT a.department_id AS 'department_id', a.num_emp / b.total_count AS 'total_count', a.sal_sum / b.total_sal AS 'total_sal' -FROM (SELECT department_id, COUNT(*) AS 'num_emp', SUM(salary) AS 'sal_sum' +SELECT a.department_id AS department_id, a.num_emp / b.total_count AS total_count, a.sal_sum / b.total_sal AS total_sal +FROM (SELECT department_id, COUNT(*) AS num_emp, SUM(salary) AS sal_sum FROM employees - GROUP BY department_id) 'a' , (SELECT COUNT(*) AS 'total_count', SUM(salary) AS 'total_sal' - FROM employees) 'b' + GROUP BY department_id) a , (SELECT COUNT(*) AS total_count, SUM(salary) AS total_sal + FROM employees) b ORDER BY a.department_id; \ No newline at end of file diff --git a/src/test/resources/bvt/parser/oracle-9.txt b/src/test/resources/bvt/parser/oracle-9.txt index 575b0dbb46..68d917c573 100644 --- a/src/test/resources/bvt/parser/oracle-9.txt +++ b/src/test/resources/bvt/parser/oracle-9.txt @@ -2,6 +2,7 @@ SELECT salary FROM employees AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '1' MINUTE) WHERE last_name = 'Chung'; --------------------------- -SELECT salary FROM employees - AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '1' MINUTE) - WHERE last_name = 'Chung'; \ No newline at end of file +SELECT salary +FROM employees +AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '1' MINUTE) +WHERE last_name = 'Chung'; \ No newline at end of file