diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectHierachicalQueryClause.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectHierachicalQueryClause.java index 89fd30af72..1ca5f6411a 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectHierachicalQueryClause.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectHierachicalQueryClause.java @@ -25,12 +25,21 @@ public class OracleSelectHierachicalQueryClause extends OracleSQLObject { private SQLExpr startWith; private SQLExpr connectBy; + private boolean prior = false; private boolean noCycle = false; public OracleSelectHierachicalQueryClause(){ } + public boolean isPrior() { + return prior; + } + + public void setPrior(boolean prior) { + this.prior = prior; + } + protected void accept0(OracleASTVisitor visitor) { if (visitor.visit(this)) { acceptChild(visitor, this.startWith); 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 7fe28ce0b5..6f154ada3b 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 @@ -561,9 +561,15 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS public boolean visit(OracleSelectHierachicalQueryClause x) { print("CONNECT BY "); + + if (x.isPrior()) { + print("PRIOR "); + } + if (x.isNoCycle()) { print("NOCYCLE "); } + x.getConnectBy().accept(this); print(" START WITH "); x.getStartWith().accept(this); 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 9661a0d0d0..7793cd1233 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 @@ -1,16 +1,102 @@ package com.alibaba.druid.sql.dialect.oracle.parser; +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.druid.sql.parser.Keywords; import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.Token; public class OracleLexer extends Lexer { + public final static Keywords DEFAULT_ORACLE_KEYWORDS; + + static { + Map map = new HashMap(); + map.put("EXISTS", Token.EXISTS); + map.put("THEN", Token.THEN); + map.put("AS", Token.AS); + map.put("GROUP", Token.GROUP); + map.put("BY", Token.BY); + map.put("HAVING", Token.HAVING); + map.put("DELETE", Token.DELETE); + map.put("ORDER", Token.ORDER); + map.put("INDEX", Token.INDEX); + map.put("FOR", Token.FOR); + map.put("SCHEMA", Token.SCHEMA); + map.put("FOREIGN", Token.FOREIGN); + map.put("REFERENCE", Token.REFERENCE); + map.put("REFERENCES", Token.REFERENCES); + map.put("CHECK", Token.CHECK); + map.put("PRIMARY", Token.PRIMARY); + map.put("KEY", Token.KEY); + map.put("CONSTRAINT", Token.CONSTRAINT); + map.put("DEFAULT", Token.DEFAULT); + map.put("VIEW", Token.VIEW); + map.put("CREATE", Token.CREATE); + map.put("VALUES", Token.VALUES); + map.put("ALTER", Token.ALTER); + map.put("TABLE", Token.TABLE); + map.put("DROP", Token.DROP); + map.put("SET", Token.SET); + map.put("INTO", Token.INTO); + map.put("UPDATE", Token.UPDATE); + map.put("NULL", Token.NULL); + map.put("IS", Token.IS); + map.put("NOT", Token.NOT); + map.put("SELECT", Token.SELECT); + map.put("INSERT", Token.INSERT); + map.put("FROM", Token.FROM); + map.put("WHERE", Token.WHERE); + map.put("AND", Token.AND); + map.put("OR", Token.OR); + map.put("XOR", Token.XOR); + map.put("DISTINCT", Token.DISTINCT); + map.put("UNIQUE", Token.UNIQUE); + map.put("ALL", Token.ALL); + map.put("UNION", Token.UNION); + map.put("INTERSECT", Token.INTERSECT); + map.put("MINUS", Token.MINUS); + map.put("INNER", Token.INNER); + map.put("LEFT", Token.LEFT); + map.put("RIGHT", Token.RIGHT); + map.put("FULL", Token.FULL); + map.put("ON", Token.ON); + map.put("OUTER", Token.OUTER); + map.put("JOIN", Token.JOIN); + map.put("NEW", Token.NEW); + map.put("CASE", Token.CASE); + map.put("WHEN", Token.WHEN); + map.put("END", Token.END); + map.put("WHEN", Token.WHEN); + map.put("ELSE", Token.ELSE); + map.put("EXISTS", Token.EXISTS); + map.put("CAST", Token.CAST); + map.put("IN", Token.IN); + map.put("ASC", Token.ASC); + map.put("DESC", Token.DESC); + map.put("LIKE", Token.LIKE); + map.put("ESCAPE", Token.ESCAPE); + map.put("BETWEEN", Token.BETWEEN); + map.put("INTERVAL", Token.INTERVAL); + map.put("LOCK", Token.LOCK); + map.put("SOME", Token.SOME); + map.put("ANY", Token.ANY); + + map.put("START", Token.START); + map.put("CONNECT", Token.CONNECT); + map.put("PRIOR", Token.PRIOR); + + DEFAULT_ORACLE_KEYWORDS = new Keywords(map); + } public OracleLexer(char[] input, int inputLength, boolean skipComment){ super(input, inputLength, skipComment); + super.keywods = DEFAULT_ORACLE_KEYWORDS; } public OracleLexer(String input){ super(input); + super.keywods = DEFAULT_ORACLE_KEYWORDS; } public void scanComment() { 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 824a9b0ce4..72983c111e 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 @@ -217,7 +217,7 @@ public class OracleSelectParser extends SQLSelectParser { hierachical.setConnectBy(this.createExprParser().expr()); } - if (identifierEquals("START")) { + if (lexer.token() == Token.START) { lexer.nextToken(); if (hierachical == null) { hierachical = new OracleSelectHierachicalQueryClause(); @@ -227,13 +227,19 @@ public class OracleSelectParser extends SQLSelectParser { hierachical.setStartWith(this.createExprParser().expr()); } - if (identifierEquals("CONNECT")) { + if (lexer.token() == Token.CONNECT) { if (hierachical == null) { hierachical = new OracleSelectHierachicalQueryClause(); } lexer.nextToken(); accept(Token.BY); + + + if (lexer.token() == Token.PRIOR) { + lexer.nextToken(); + hierachical.setPrior(true); + } if (identifierEquals("NOCYCLE")) { hierachical.setNoCycle(true); 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 fb7a2ba712..08e4749190 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Token.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Token.java @@ -93,8 +93,14 @@ public enum Token { SOME("SOME"), ANY("ANY"), + // mysql TRUE("TRUE"), FALSE("FALSE"), + + // oracle + START("START"), + PRIOR("PRIOR"), + CONNECT("CONNECT"), EOF, ERROR, IDENTIFIER, diff --git a/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSQLParserTest.java b/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSQLParserTest.java index 712d175ca7..a32a1f4aac 100644 --- a/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSQLParserTest.java +++ b/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSQLParserTest.java @@ -50,6 +50,18 @@ public class OracleSQLParserTest extends TestCase { String text = output(statementList); System.out.println(text); } + + public void test_2() throws Exception { + String sql = "SELECT LPAD(' ',2*(LEVEL-1)) || last_name org_chart, employee_id, manager_id, job_id FROM employees WHERE job_id != 'FI_MGR' START WITH job_id = 'AD_VP' CONNECT BY PRIOR employee_id = manager_id; "; + + OracleStatementParser parser = new OracleStatementParser(sql); + List statementList = parser.parseStatementList(); + + Assert.assertEquals(1, statementList.size()); + + String text = output(statementList); + System.out.println(text); + } private String output(List stmtList) { StringBuilder out = new StringBuilder();