diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleIntervalExpr.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleIntervalExpr.java old mode 100644 new mode 100755 index 67928f8ae0..2aec737230 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleIntervalExpr.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleIntervalExpr.java @@ -15,6 +15,7 @@ */ package com.alibaba.druid.sql.dialect.oracle.ast.expr; +import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; @@ -22,7 +23,7 @@ import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class OracleIntervalExpr extends SQLLiteralExpr { private static final long serialVersionUID = 1L; - private String value; + private SQLExpr value; private OracleIntervalType type; private Integer precision; private Integer factionalSecondsPrecision; @@ -33,11 +34,11 @@ public class OracleIntervalExpr extends SQLLiteralExpr { } - public String getValue() { + public SQLExpr getValue() { return this.value; } - public void setValue(String value) { + public void setValue(SQLExpr value) { this.value = value; } 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 209321804e..746272ee44 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 @@ -19,6 +19,7 @@ import java.math.BigInteger; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLOrderingSpecification; +import com.alibaba.druid.sql.ast.expr.SQLCharExpr; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; import com.alibaba.druid.sql.ast.expr.SQLNumberExpr; @@ -57,6 +58,35 @@ public class OracleExprParser extends SQLExprParser { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + public boolean allowStringAdditive = false; /** @@ -240,6 +270,44 @@ public class OracleExprParser extends SQLExprParser { expr = dblink; } + if (lexer.token() == Token.DAY || lexer.token() == Token.YEAR) { + OracleIntervalExpr interval = new OracleIntervalExpr(); + interval.setValue(expr); + OracleIntervalType type = OracleIntervalType.valueOf(lexer.token().name); + interval.setType(type); + lexer.nextToken(); + + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + if (lexer.token() != Token.LITERAL_INT) { + throw new ParserException("syntax error"); + } + interval.setPrecision(lexer.integerValue().intValue()); + lexer.nextToken(); + accept(Token.RPAREN); + } + + accept(Token.TO); + if (lexer.token() == Token.SECOND) { + lexer.nextToken(); + interval.setToType(OracleIntervalType.SECOND); + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + if (lexer.token() != Token.LITERAL_INT) { + throw new ParserException("syntax error"); + } + interval.setFactionalSecondsPrecision(lexer.integerValue().intValue()); + lexer.nextToken(); + accept(Token.RPAREN); + } + } else { + interval.setToType(OracleIntervalType.MONTH); + lexer.nextToken(); + } + + expr = interval; + } + return super.primaryRest(expr); } @@ -458,16 +526,53 @@ public class OracleExprParser extends SQLExprParser { if (lexer.token() != Token.LITERAL_CHARS) { throw new ParserException("syntax error : " + lexer.token()); } - interval.setValue(lexer.stringVal()); + interval.setValue(new SQLCharExpr(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; + + OracleIntervalType type = OracleIntervalType.valueOf(lexer.token().name); + interval.setType(type); + lexer.nextToken(); + + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + if (lexer.token() != Token.LITERAL_INT) { + throw new ParserException("syntax error"); + } + interval.setPrecision(lexer.integerValue().intValue()); + lexer.nextToken(); + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + if (lexer.token() != Token.LITERAL_INT) { + throw new ParserException("syntax error"); + } + interval.setFactionalSecondsPrecision(lexer.integerValue().intValue()); + lexer.nextToken(); + } + accept(Token.RPAREN); + } + + if (lexer.token() == Token.TO) { + accept(Token.TO); + if (lexer.token() == Token.SECOND) { + lexer.nextToken(); + interval.setToType(OracleIntervalType.SECOND); + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + if (lexer.token() != Token.LITERAL_INT) { + throw new ParserException("syntax error"); + } + interval.setToFactionalSecondsPrecision(lexer.integerValue().intValue()); + lexer.nextToken(); + accept(Token.RPAREN); + } + } else { + interval.setToType(OracleIntervalType.MONTH); + lexer.nextToken(); + } + } + + return interval; } } 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 1b88ef3156..b6778566b3 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 @@ -95,6 +95,12 @@ public class OracleLexer extends Lexer { map.put("TIMESTAMP", Token.TIMESTAMP); map.put("CURSOR", Token.CURSOR); + + map.put("DAY", Token.DAY); + map.put("YEAR", Token.YEAR); + map.put("MONTH", Token.MONTH); + map.put("SECOND", Token.SECOND); + map.put("TO", Token.TO); DEFAULT_ORACLE_KEYWORDS = new Keywords(map); } 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 48a2d47e72..e43f2f881e 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 @@ -24,6 +24,7 @@ import com.alibaba.druid.sql.ast.SQLObject; import com.alibaba.druid.sql.ast.SQLSetQuantifier; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; +import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr; import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.expr.SQLObjectCreateExpr; import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource.JoinType; @@ -241,12 +242,19 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS } public boolean visit(OracleIntervalExpr x) { - print("INTERVAL '"); - print(x.getValue()); - print("' "); + if (x.getValue() instanceof SQLLiteralExpr) { + print("INTERVAL "); + x.getValue().accept(this); + print(" "); + } else { + print('('); + x.getValue().accept(this); + print(") "); + } + print(x.getType().name()); - if ((x.getPrecision() != null) || (x.getFactionalSecondsPrecision() != null)) { + if (x.getPrecision() != null) { print("("); print(x.getPrecision().intValue()); if (x.getFactionalSecondsPrecision() != null) { @@ -258,10 +266,10 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS if (x.getToType() != null) { print(" TO "); - print(x.getType().name()); + print(x.getToType().name()); if (x.getToFactionalSecondsPrecision() != null) { print("("); - print(x.getFactionalSecondsPrecision().intValue()); + print(x.getToFactionalSecondsPrecision().intValue()); print(")"); } } @@ -1152,6 +1160,6 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS @Override public void endVisit(OracleCursorExpr x) { - + } } diff --git a/src/main/java/com/alibaba/druid/sql/parser/Token.java b/src/main/java/com/alibaba/druid/sql/parser/Token.java index 5008a82f4e..e78c4fc5c7 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Token.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Token.java @@ -106,7 +106,12 @@ public enum Token { EXTRACT("EXTRACT"), TIMESTAMP("TIMESTAMP"), DATE("DATE"), - CURSOR("CURSOR"), + CURSOR("SECOND"), + DAY("DAY"), + YEAR("YEAR"), + MONTH("MONTH"), + SECOND("SECOND"), + TO("TO"), EOF, ERROR, diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleIntervalTest.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleIntervalTest.java new file mode 100644 index 0000000000..d4322289b9 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleIntervalTest.java @@ -0,0 +1,102 @@ +package com.alibaba.druid.bvt.sql.oracle; + +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; + +import junit.framework.Assert; +import junit.framework.TestCase; + +public class OracleIntervalTest extends TestCase { + + public void test_interval_literal() throws Exception { + String sql = "SELECT INTERVAL '123-2' YEAR(3) TO MONTH FROM DUAL"; + + OracleStatementParser parser = new OracleStatementParser(sql); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatementList().get(0); + + String text = TestUtils.output(stmt); + + Assert.assertEquals("SELECT INTERVAL '123-2' YEAR(3) TO MONTH\nFROM DUAL;\n", text); + + System.out.println(text); + } + + public void test_interval_literal_1() throws Exception { + String sql = "SELECT INTERVAL '123' YEAR(3) FROM DUAL"; + + OracleStatementParser parser = new OracleStatementParser(sql); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatementList().get(0); + + String text = TestUtils.output(stmt); + + Assert.assertEquals("SELECT INTERVAL '123' YEAR(3)\nFROM DUAL;\n", text); + + System.out.println(text); + } + + public void test_interval_literal_2() throws Exception { + String sql = "SELECT INTERVAL '5-3' YEAR TO MONTH + INTERVAL'20' MONTH FROM DUAL"; + + OracleStatementParser parser = new OracleStatementParser(sql); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatementList().get(0); + + String text = TestUtils.output(stmt); + + Assert.assertEquals("SELECT INTERVAL '5-3' YEAR TO MONTH + INTERVAL '20' MONTH\nFROM DUAL;\n", text); + + System.out.println(text); + } + + public void test_interval_literal_3() throws Exception { + String sql = "SELECT INTERVAL '6-11' YEAR TO MONTH FROM DUAL"; + + OracleStatementParser parser = new OracleStatementParser(sql); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatementList().get(0); + + String text = TestUtils.output(stmt); + + Assert.assertEquals("SELECT INTERVAL '6-11' YEAR TO MONTH\nFROM DUAL;\n", text); + + System.out.println(text); + } + + public void test_interval_literal_4() throws Exception { + String sql = "SELECT INTERVAL '4 5:12:10.222' DAY TO SECOND(3) FROM DUAL"; + + OracleStatementParser parser = new OracleStatementParser(sql); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatementList().get(0); + + String text = TestUtils.output(stmt); + + Assert.assertEquals("SELECT INTERVAL '4 5:12:10.222' DAY TO SECOND(3)\nFROM DUAL;\n", text); + + System.out.println(text); + } + + public void test_interval_literal_5() throws Exception { + String sql = "SELECT INTERVAL '30.12345' SECOND(2,4) FROM DUAL"; + + OracleStatementParser parser = new OracleStatementParser(sql); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatementList().get(0); + + String text = TestUtils.output(stmt); + + Assert.assertEquals("SELECT INTERVAL '30.12345' SECOND(2, 4)\nFROM DUAL;\n", text); + + System.out.println(text); + } + + public void test_interval() throws Exception { + String sql = "SELECT (SYSTIMESTAMP - order_date) DAY(9) TO SECOND from orders WHERE order_id = 2458;"; + + OracleStatementParser parser = new OracleStatementParser(sql); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatementList().get(0); + + String text = TestUtils.output(stmt); + + Assert.assertEquals("SELECT (SYSTIMESTAMP - order_date) DAY(9) TO SECOND\n" + "FROM orders\n" + + "WHERE order_id = 2458;\n", text); + + System.out.println(text); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleNumberLiteralTest.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleNumberLiteralTest.java new file mode 100644 index 0000000000..d92aa5a0bf --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleNumberLiteralTest.java @@ -0,0 +1,36 @@ +package com.alibaba.druid.bvt.sql.oracle; + +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; + +import junit.framework.Assert; +import junit.framework.TestCase; + +public class OracleNumberLiteralTest extends TestCase { + + public void test_number_literal() throws Exception { + String sql = "SELECT 7, +255, 0.5, +6.34,25e-03, +6.34F, 0.5d, -1D FROM DUAL"; + + OracleStatementParser parser = new OracleStatementParser(sql); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatementList().get(0); + + String text = TestUtils.output(stmt); + + Assert.assertEquals("SELECT 7, 255, 0.5, 6.34, 0.025\n\t, 6.34F, 0.5D, -1.0D\nFROM DUAL;\n", text); + + System.out.println(text); + } + + public void test_number_literal_2() throws Exception { + String sql = "SELECT BINARY_FLOAT_INFINITY FROM DUAL"; + + OracleStatementParser parser = new OracleStatementParser(sql); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatementList().get(0); + + String text = TestUtils.output(stmt); + + Assert.assertEquals("SELECT BINARY_FLOAT_INFINITY\nFROM DUAL;\n", text); + + System.out.println(text); + } +}