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 dc2708fe2b..95a84ed47a 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 @@ -236,11 +236,20 @@ public class OracleExprParser extends SQLExprParser { } if (lexer.token() == Token.DAY || lexer.token() == Token.YEAR) { + lexer.mark(); + + String name = lexer.token().name; + lexer.nextToken(); + + if (lexer.token() == Token.COMMA) { + lexer.reset(); + return expr; + } + OracleIntervalExpr interval = new OracleIntervalExpr(); interval.setValue(expr); - OracleIntervalType type = OracleIntervalType.valueOf(lexer.token().name); + OracleIntervalType type = OracleIntervalType.valueOf(name); interval.setType(type); - lexer.nextToken(); if (lexer.token() == Token.LPAREN) { lexer.nextToken(); diff --git a/src/main/java/com/alibaba/druid/sql/parser/Lexer.java b/src/main/java/com/alibaba/druid/sql/parser/Lexer.java index 521479f3bf..969df58c9d 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Lexer.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Lexer.java @@ -76,10 +76,39 @@ public class Lexer { protected boolean skipComment = true; + private SavePoint savePoint = null; + public Lexer(String input){ this(input, true); } + private static class SavePoint { + + int bp; + int sp; + int np; + char ch; + Token token; + } + + public void mark() { + SavePoint savePoint = new SavePoint(); + savePoint.bp = bp; + savePoint.sp = sp; + savePoint.np = np; + savePoint.ch = ch; + savePoint.token = token; + this.savePoint = savePoint; + } + + public void reset() { + this.bp = savePoint.bp; + this.sp = savePoint.sp; + this.np = savePoint.np; + this.ch = savePoint.ch; + this.token = savePoint.token; + } + public Lexer(String input, boolean skipComment){ this(input.toCharArray(), input.length(), skipComment); } 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 51d3b1eb25..4ba52cb485 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLParser.java @@ -41,7 +41,7 @@ public class SQLParser { } } - protected final String as() throws ParserException { + protected String as() throws ParserException { String alias = null; if (lexer.token() == Token.AS) { @@ -65,6 +65,12 @@ public class SQLParser { return alias; } + if (lexer.token() == Token.YEAR || lexer.token() == Token.DAY) { + alias = lexer.token.name(); + lexer.nextToken(); + return alias; + } + switch (lexer.token()) { case KEY: alias = lexer.token().name(); @@ -86,6 +92,9 @@ public class SQLParser { } else if (lexer.token() == Token.LITERAL_CHARS) { alias = "'" + lexer.stringVal() + "'"; lexer.nextToken(); + } else if (lexer.token() == Token.YEAR || lexer.token() == Token.DAY) { + alias = lexer.token.name(); + lexer.nextToken(); } switch (lexer.token()) { diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OraclePivotClauseTest.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OraclePivotClauseTest.java new file mode 100644 index 0000000000..50d783bee7 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OraclePivotClauseTest.java @@ -0,0 +1,106 @@ +package com.alibaba.druid.bvt.sql.oracle; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; +import com.alibaba.druid.sql.test.TestUtils; + +public class OraclePivotClauseTest extends TestCase { + + public void test_0() throws Exception { + String sql = "SELECT *\n" + "FROM pivot_table\n" + + "UNPIVOT (yearly_total FOR order_mode IN (store AS 'direct', internet AS 'online'))\n" + + "ORDER BY year, order_mode;"; + + String expected = "SELECT *\n" + "FROM pivot_table\n" + + "\tUNPIVOT (yearly_total FOR order_mode IN (store AS 'direct', internet AS 'online'))\n" + + "ORDER BY YEAR, order_mode;\n"; + + OracleStatementParser parser = new OracleStatementParser(sql); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatementList().get(0); + + String text = TestUtils.outputOracle(stmt); + + Assert.assertEquals(expected, text); + + System.out.println(text); + } + + public void test_pivot() throws Exception { + String sql = "SELECT *\n" + "FROM (SELECT EXTRACT(YEAR FROM order_date) year, order_mode, order_total FROM orders)\n" + + "PIVOT (SUM(order_total) FOR order_mode IN ('direct' AS Store, 'online' AS Internet));\n"; + + String expected = "SELECT *\n" + "FROM (SELECT EXTRACT(YEAR FROM order_date) AS YEAR, order_mode, order_total\n" + + "\tFROM orders\n" + + "\t)\n" + + "\tPIVOT (SUM(order_total) FOR order_mode IN ('direct' AS Store, 'online' AS Internet));\n"; + + OracleStatementParser parser = new OracleStatementParser(sql); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatementList().get(0); + + String text = TestUtils.outputOracle(stmt); + + Assert.assertEquals(expected, text); + + System.out.println(text); + } + + public void test_pivot_1() throws Exception { + String sql = "SELECT *\n" + "FROM (SELECT EXTRACT(YEAR FROM order_date) as year, order_mode, order_total FROM orders)\n" + + "PIVOT (SUM(order_total) FOR order_mode IN ('direct' AS Store, 'online' AS Internet));\n"; + + String expected = "SELECT *\n" + "FROM (SELECT EXTRACT(YEAR FROM order_date) AS YEAR, order_mode, order_total\n" + + "\tFROM orders\n" + + "\t)\n" + + "\tPIVOT (SUM(order_total) FOR order_mode IN ('direct' AS Store, 'online' AS Internet));\n"; + + OracleStatementParser parser = new OracleStatementParser(sql); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatementList().get(0); + + String text = TestUtils.outputOracle(stmt); + + Assert.assertEquals(expected, text); + + System.out.println(text); + } + + public void test_pivot_2() throws Exception { + String sql = "SELECT *\n" + "FROM (SELECT EXTRACT(YEAR FROM order_date) as day, order_mode, order_total FROM orders)\n" + + "PIVOT (SUM(order_total) FOR order_mode IN ('direct' AS Store, 'online' AS Internet));\n"; + + String expected = "SELECT *\n" + "FROM (SELECT EXTRACT(YEAR FROM order_date) AS DAY, order_mode, order_total\n" + + "\tFROM orders\n" + + "\t)\n" + + "\tPIVOT (SUM(order_total) FOR order_mode IN ('direct' AS Store, 'online' AS Internet));\n"; + + OracleStatementParser parser = new OracleStatementParser(sql); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatementList().get(0); + + String text = TestUtils.outputOracle(stmt); + + Assert.assertEquals(expected, text); + + System.out.println(text); + } + + public void test_pivot_3() throws Exception { + String sql = "SELECT *\n" + "FROM (SELECT EXTRACT(YEAR FROM order_date) day, order_mode YEAR, order_total FROM orders)\n" + + "PIVOT (SUM(order_total) FOR order_mode IN ('direct' AS Store, 'online' AS Internet));\n"; + + String expected = "SELECT *\n" + "FROM (SELECT EXTRACT(YEAR FROM order_date) AS DAY, order_mode AS YEAR, order_total\n" + + "\tFROM orders\n" + + "\t)\n" + + "\tPIVOT (SUM(order_total) FOR order_mode IN ('direct' AS Store, 'online' AS Internet));\n"; + + OracleStatementParser parser = new OracleStatementParser(sql); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatementList().get(0); + + String text = TestUtils.outputOracle(stmt); + + Assert.assertEquals(expected, text); + + System.out.println(text); + } +}