diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelect.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelect.java index dc18f4faa5..496e1a5651 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelect.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelect.java @@ -17,6 +17,7 @@ package com.alibaba.druid.sql.dialect.oracle.ast.stmt; import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.dialect.oracle.ast.visitor.OracleASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class OracleSelect extends SQLSelect { @@ -51,6 +52,22 @@ public class OracleSelect extends SQLSelect { if (this.orderBy != null) this.orderBy.output(buf); } + + protected void accept0(SQLASTVisitor visitor) { + if (visitor instanceof OracleASTVisitor) { + accept0((OracleASTVisitor) visitor); + return; + } + + if (visitor.visit(this)) { + acceptChild(visitor, this.query); + acceptChild(visitor, this.restriction); + acceptChild(visitor, this.orderBy); + acceptChild(visitor, this.forUpdate); + } + + visitor.endVisit(this); + } protected void accept0(OracleASTVisitor visitor) { if (visitor.visit(this)) { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java index a3e5a38504..c808993d81 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java @@ -51,7 +51,18 @@ public class OracleSelectQueryBlock extends SQLSelectQueryBlock { @Override protected void accept0(SQLASTVisitor visitor) { - this.accept0((OracleASTVisitor) visitor); + if (visitor instanceof OracleASTVisitor) { + accept0((OracleASTVisitor) visitor); + return; + } + + if (visitor.visit(this)) { + acceptChild(visitor, this.selectList); + acceptChild(visitor, this.from); + acceptChild(visitor, this.where); + acceptChild(visitor, this.groupBy); + } + visitor.endVisit(this); } protected void accept0(OracleASTVisitor visitor) { 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 8995edf922..7bdaaaeac5 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 @@ -378,7 +378,8 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS } public boolean visit(OracleHint x) { - throw new UnsupportedOperationException(); + print(x.getName()); + return false; } public boolean visit(OracleInsertStatement x) { @@ -655,12 +656,20 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS public boolean visit(OracleSelectQueryBlock select) { print("SELECT "); - if (SQLSetQuantifier.ALL == select.getDistionOption()) print("ALL "); - else if (SQLSetQuantifier.DISTINCT == select.getDistionOption()) print("DISTINCT "); - else if (SQLSetQuantifier.UNIQUE == select.getDistionOption()) { + if (SQLSetQuantifier.ALL == select.getDistionOption()) { + print("ALL "); + } else if (SQLSetQuantifier.DISTINCT == select.getDistionOption()) { + print("DISTINCT "); + } else if (SQLSetQuantifier.UNIQUE == select.getDistionOption()) { print("UNIQUE "); } - + + if (select.getHints().size() > 0) { + print("/*+"); + printAndAccept(select.getHints(), ", "); + print("*/ "); + } + printAndAccept(select.getSelectList(), ", "); println(); 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 new file mode 100644 index 0000000000..9661a0d0d0 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java @@ -0,0 +1,99 @@ +package com.alibaba.druid.sql.dialect.oracle.parser; + +import com.alibaba.druid.sql.parser.Lexer; +import com.alibaba.druid.sql.parser.Token; + +public class OracleLexer extends Lexer { + + public OracleLexer(char[] input, int inputLength, boolean skipComment){ + super(input, inputLength, skipComment); + } + + public OracleLexer(String input){ + super(input); + } + + public void scanComment() { + if (ch != '/') { + throw new IllegalStateException(); + } + + np = bp; + sp = 0; + scanChar(); + sp++; + + // /*+ */ + if (ch == '*') { + scanChar(); + sp++; + + while (ch == ' ') { + scanChar(); + sp++; + } + + boolean isHint = false; + int startHintSp = sp + 1; + if (ch == '+') { + isHint = true; + scanChar(); + sp++; + } + + for (;;) { + if (ch == '*' && buf[bp + 1] == '/') { + sp += 2; + scanChar(); + scanChar(); + break; + } + + scanChar(); + sp++; + } + + + + if (isHint) { + stringVal = new String(buf, np + startHintSp, (sp - startHintSp) - 2).trim(); + token = Token.HINT; + } else { + stringVal = new String(buf, np, sp); + token = Token.MULTI_LINE_COMMENT; + } + + return; + } + + if (ch == '/') { + scanChar(); + sp++; + + for (;;) { + if (ch == '\r') { + if (buf[bp + 1] == '\n') { + sp += 2; + scanChar(); + break; + } + sp++; + break; + } + + if (ch == '\r') { + scanChar(); + sp++; + break; + } + + scanChar(); + sp++; + } + + stringVal = new String(buf, np + 1, sp); + token = Token.LINE_COMMENT; + return; + } + } +} 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 e0dec5982e..824a9b0ce4 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 @@ -21,6 +21,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause; import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; 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.expr.OracleAggregateExpr; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelect; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectForUpdate; @@ -129,9 +130,11 @@ public class OracleSelectParser extends SQLSelectParser { OracleSelectQueryBlock queryBlock = new OracleSelectQueryBlock(); parseHints(queryBlock); - if (lexer.token() == (Token.DISTINCT)) queryBlock.setDistionOption(SQLSetQuantifier.DISTINCT); - else if (lexer.token() == (Token.UNIQUE)) queryBlock.setDistionOption(SQLSetQuantifier.UNIQUE); - else if (lexer.token() == (Token.ALL)) { + if (lexer.token() == (Token.DISTINCT)) { + queryBlock.setDistionOption(SQLSetQuantifier.DISTINCT); + } else if (lexer.token() == (Token.UNIQUE)) { + queryBlock.setDistionOption(SQLSetQuantifier.UNIQUE); + } else if (lexer.token() == (Token.ALL)) { queryBlock.setDistionOption(SQLSetQuantifier.ALL); } @@ -484,6 +487,9 @@ public class OracleSelectParser extends SQLSelectParser { } private void parseHints(OracleSelectQueryBlock queryBlock) { - if (lexer.token() == Token.HINT) throw new ParserException("TODO"); + if (lexer.token() == Token.HINT) { + queryBlock.getHints().add(new OracleHint(lexer.stringVal())); + lexer.nextToken(); + } } } 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 25ff06fae3..1c251028a7 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 @@ -30,7 +30,8 @@ import com.alibaba.druid.sql.parser.Token; public class OracleStatementParser extends SQLStatementParser { public OracleStatementParser(String sql){ - super(sql); + super(new OracleLexer(sql)); + this.lexer.nextToken(); } public OracleStatementParser(Lexer lexer){ @@ -97,7 +98,7 @@ public class OracleStatementParser extends SQLStatementParser { throw new ParserException("TODO"); } - throw new ParserException("TODO"); + throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal()); } } diff --git a/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSampleTest1.java b/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSampleTest1.java new file mode 100644 index 0000000000..9b6795d5da --- /dev/null +++ b/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSampleTest1.java @@ -0,0 +1,23 @@ +package com.alibaba.druid.sql.oracle.bvt; + +import java.util.List; + +import junit.framework.TestCase; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor; + +public class OracleSampleTest1 extends TestCase { + public void test_0 () throws Exception { + + } + + private void output(List stmtList) { + for (SQLStatement stmt : stmtList) { + stmt.accept(new MySqlOutputVisitor(System.out)); + System.out.println(";"); + System.out.println(); + } + } + +} diff --git a/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSelectParserTest.java b/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSelectParserTest.java index 2ae61e43c6..fc7bbcfae0 100644 --- a/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSelectParserTest.java +++ b/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSelectParserTest.java @@ -20,30 +20,38 @@ import java.util.List; import junit.framework.TestCase; import com.alibaba.druid.sql.ast.SQLStatement; -import com.alibaba.druid.sql.ast.statement.SQLSelect; -import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor; import com.alibaba.druid.sql.dialect.oracle.ast.visitor.OracleOutputVisitor; -import com.alibaba.druid.sql.dialect.oracle.parser.OracleSelectParser; -import com.alibaba.druid.sql.parser.SQLSelectParser; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; public class OracleSelectParserTest extends TestCase { - public void test_select() throws Exception { - String sql = "SELECT TRIM('xxxxxxx') FROM `T_USER` WHERE FID >= 100 AND ROWNUM < 10"; - SQLSelectParser parser = new OracleSelectParser(sql); - SQLSelect select = parser.select(); + public void f_test_select() throws Exception { + String sql = "SELECT last_name, department_id FROM employees WHERE department_id = (SELECT department_id FROM employees WHERE last_name = 'Lorentz') ORDER BY last_name, department_id;"; - select.accept(new OracleOutputVisitor(System.out)); - System.out.println(); - System.out.println(select); + OracleStatementParser parser = new OracleStatementParser(sql); + List statementList = parser.parseStatementList(); + + output(statementList); + } + + public void test_hinits() throws Exception { + String sql = "SELECT /*+FIRST_ROWS*/ * FROM T"; + + OracleStatementParser parser = new OracleStatementParser(sql); + List statementList = parser.parseStatementList(); + + output(statementList); } private void output(List stmtList) { - for (SQLStatement stmt : stmtList) { - stmt.accept(new MySqlOutputVisitor(System.out)); - System.out.println(";"); - System.out.println(); - } - } + StringBuilder out = new StringBuilder(); + OracleOutputVisitor visitor = new OracleOutputVisitor(out); + for (SQLStatement stmt : stmtList) { + stmt.accept(visitor); + visitor.println(); + } + + System.out.println(out.toString()); + } }