diff --git a/sqlTranslate/.idea/workspace.xml b/sqlTranslate/.idea/workspace.xml index e8d02f402..be45680e5 100644 --- a/sqlTranslate/.idea/workspace.xml +++ b/sqlTranslate/.idea/workspace.xml @@ -4,7 +4,14 @@ - - @@ -484,7 +489,9 @@ - diff --git a/sqlTranslate/src/main/java/Lexer/OracleLexer.java b/sqlTranslate/src/main/java/Lexer/OracleLexer.java index c96034ca2..04e721e23 100644 --- a/sqlTranslate/src/main/java/Lexer/OracleLexer.java +++ b/sqlTranslate/src/main/java/Lexer/OracleLexer.java @@ -18,7 +18,7 @@ public class OracleLexer { // keywords of drop table , "DROP", "CASCADE" // keywords of select - , "DISTINCT", "JOIN", "GROUP BY", "ORDER BY", "HAVING", "UNION", "CASE", "WHEN", "END", "AS" + , "DISTINCT", "JOIN", "GROUP BY", "ORDER BY", "HAVING", "UNION", "CASE", "WHEN", "END", "AS", "||" // keywords of join , "INNER JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN", "FULL JOIN", "FULL OUTER JOIN", "CROSS JOIN", "JOIN", "ON", "USING" // keywords of case when @@ -45,6 +45,7 @@ public class OracleLexer { "(AVG\\(.*?\\))|" + // AVG() function "(MAX\\(.*?\\))|" + // MAX() function "(MIN\\(.*?\\))|" + // MIN() function + "(\\|\\|\\s*'.*?'\\s*\\|\\|)|" + // || '*' || "(NOT NULL)|" + "(PRIMARY KEY)|" + @@ -66,6 +67,7 @@ public class OracleLexer { "(FULL JOIN)|" + "(FULL OUTER JOIN)|" + "(CROSS JOIN)|" + + "(\\|\\|)|" + "(\\b[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*\\b)|" + // Keywords and identifiers @@ -122,40 +124,43 @@ public class OracleLexer { else if (tokenValue.matches("(?i)NUMBER\\(.*?\\)")) { // NUMBER() function, CASE_INSENSITIVE return new Token(Token.TokenType.KEYWORD, tokenValue); - }else if (tokenValue.matches("(?i)DECIMAL\\(.*?\\)")) { + } else if (tokenValue.matches("(?i)DECIMAL\\(.*?\\)")) { // DECIMAL() function, CASE_INSENSITIVE return new Token(Token.TokenType.KEYWORD, tokenValue); - }else if (tokenValue.matches("(?i)FLOAT\\(.*?\\)")) { + } else if (tokenValue.matches("(?i)FLOAT\\(.*?\\)")) { // FLOAT() function, CASE_INSENSITIVE return new Token(Token.TokenType.KEYWORD, tokenValue); - }else if (tokenValue.matches("(?i)CHAR\\(.*?\\)")) { + } else if (tokenValue.matches("(?i)CHAR\\(.*?\\)")) { // CHAR() function, CASE_INSENSITIVE return new Token(Token.TokenType.KEYWORD, tokenValue); - }else if (tokenValue.matches("(?i)VARCHAR2\\(.*?\\)")) { + } else if (tokenValue.matches("(?i)VARCHAR2\\(.*?\\)")) { // VARCHAR2() function, CASE_INSENSITIVE return new Token(Token.TokenType.KEYWORD, tokenValue); - }else if (tokenValue.matches("(?i)NCHAR\\(.*?\\)")) { + } else if (tokenValue.matches("(?i)NCHAR\\(.*?\\)")) { // NCHAR() function, CASE_INSENSITIVE return new Token(Token.TokenType.KEYWORD, tokenValue); - }else if (tokenValue.matches("(?i)NVARCHAR2\\(.*?\\)")) { + } else if (tokenValue.matches("(?i)NVARCHAR2\\(.*?\\)")) { // NVARCHAR2() function, CASE_INSENSITIVE return new Token(Token.TokenType.KEYWORD, tokenValue); - }else if (tokenValue.matches("(?i)RAW\\(.*?\\)")) { + } else if (tokenValue.matches("(?i)RAW\\(.*?\\)")) { // RAW() function, CASE_INSENSITIVE return new Token(Token.TokenType.KEYWORD, tokenValue); - }else if (tokenValue.matches("(?i)COUNT\\(.*?\\)")) { + } else if (tokenValue.matches("(?i)COUNT\\(.*?\\)")) { // RAW() function, CASE_INSENSITIVE return new Token(Token.TokenType.KEYWORD, tokenValue); - }else if (tokenValue.matches("(?i)SUM\\(.*?\\)")) { + } else if (tokenValue.matches("(?i)SUM\\(.*?\\)")) { // RAW() function, CASE_INSENSITIVE return new Token(Token.TokenType.KEYWORD, tokenValue); - }else if (tokenValue.matches("(?i)AVG\\(.*?\\)")) { + } else if (tokenValue.matches("(?i)AVG\\(.*?\\)")) { // RAW() function, CASE_INSENSITIVE return new Token(Token.TokenType.KEYWORD, tokenValue); - }else if (tokenValue.matches("(?i)MAX\\(.*?\\)")) { + } else if (tokenValue.matches("(?i)MAX\\(.*?\\)")) { // RAW() function, CASE_INSENSITIVE return new Token(Token.TokenType.KEYWORD, tokenValue); - }else if (tokenValue.matches("(?i)MIN\\(.*?\\)")) { + } else if (tokenValue.matches("(?i)MIN\\(.*?\\)")) { + // RAW() function, CASE_INSENSITIVE + return new Token(Token.TokenType.KEYWORD, tokenValue); + } else if (tokenValue.matches("\\|\\|\\s*'.*?'\\s*\\|\\|")) { // RAW() function, CASE_INSENSITIVE return new Token(Token.TokenType.KEYWORD, tokenValue); } diff --git a/sqlTranslate/src/main/java/Main.java b/sqlTranslate/src/main/java/Main.java index ba193d911..b2bb58bed 100644 --- a/sqlTranslate/src/main/java/Main.java +++ b/sqlTranslate/src/main/java/Main.java @@ -30,8 +30,9 @@ public class Main { // " FROM departments d\n" + // " WHERE d.department_name = 'Sales'\n" + // ");"; - String sql = "ALTER TABLE employees ADD email VARCHAR2(100) Check (email != '12813@163.com');"; +// String sql = "ALTER TABLE employees ADD email VARCHAR2(100) Check (email != '12813@163.com');"; // String sql = "CASE WHEN column2 > 0 THEN 'Positive' ELSE 'Non-positive' END"; + String sql = "CREATE OR REPLACE VIEW emp_dept_info AS SELECT e.first_name, e.last_name, d.department_name FROM employees e JOIN departments d ON e.department_id = d.department_id;"; OracleLexer lexer = new OracleLexer(sql); lexer.printTokens(); OracleParser parser = new OracleParser(lexer); diff --git a/sqlTranslate/src/main/java/Parser/AST/View/ViewNode.java b/sqlTranslate/src/main/java/Parser/AST/View/ViewCreateNode.java similarity index 67% rename from sqlTranslate/src/main/java/Parser/AST/View/ViewNode.java rename to sqlTranslate/src/main/java/Parser/AST/View/ViewCreateNode.java index 397a53c66..a76923eec 100644 --- a/sqlTranslate/src/main/java/Parser/AST/View/ViewNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/View/ViewCreateNode.java @@ -5,12 +5,17 @@ import Parser.AST.ASTNode; import java.util.List; -public class ViewNode extends ASTNode { - public ViewNode(ASTNode node) { +public class ViewCreateNode extends ASTNode { + + public ViewCreateNode() { + super(); + } + + public ViewCreateNode(ASTNode node) { super(node); } - public ViewNode(List tokens) { + public ViewCreateNode(List tokens) { super(tokens); } diff --git a/sqlTranslate/src/main/java/Parser/AST/View/ViewEndNode.java b/sqlTranslate/src/main/java/Parser/AST/View/ViewEndNode.java index e4137b346..c384900c2 100644 --- a/sqlTranslate/src/main/java/Parser/AST/View/ViewEndNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/View/ViewEndNode.java @@ -6,6 +6,10 @@ import Parser.AST.ASTNode; import java.util.List; public class ViewEndNode extends ASTNode { + public ViewEndNode() { + super(); + } + public ViewEndNode(ASTNode node) { super(node); } diff --git a/sqlTranslate/src/main/java/Parser/OracleParser.java b/sqlTranslate/src/main/java/Parser/OracleParser.java index 32d08391e..b9dd3a6a6 100644 --- a/sqlTranslate/src/main/java/Parser/OracleParser.java +++ b/sqlTranslate/src/main/java/Parser/OracleParser.java @@ -25,6 +25,10 @@ import Parser.AST.Insert.InsertObjNode; import Parser.AST.Join.*; import Parser.AST.Select.*; import Parser.AST.Update.*; +import Parser.AST.View.ViewCreateNode; +import Parser.AST.View.ViewEndNode; +import Parser.AST.View.ViewNameNode; +import Parser.AST.View.ViewTargetNode; import java.util.Stack; @@ -38,38 +42,41 @@ public class OracleParser { } public ASTNode parse() { - // check if the input is a create table statement - if ((lexer.getTokens().get(0).getValue().equalsIgnoreCase("CREATE") && lexer.getTokens().get(1).getValue().equalsIgnoreCase("TABLE")) || - (lexer.getTokens().get(0).getValue().equalsIgnoreCase("CREATE") && lexer.getTokens().get(2).getValue().equalsIgnoreCase("TEMPORARY") && lexer.getTokens().get(3).getValue().equalsIgnoreCase("TABLE")) ) { - return parseCreateTab(lexer.getTokens()); - } - else if (lexer.getTokens().get(0).getValue().equalsIgnoreCase("INSERT")) { - return parseInsert(lexer.getTokens()); - } - else if (lexer.getTokens().get(0).getValue().equalsIgnoreCase("DROP")) { - return parseDrop(lexer.getTokens()); - } - else if (lexer.getTokens().get(0).getValue().equalsIgnoreCase("SELECT")) { - return parseSelect(lexer.getTokens()); - } - else if (lexer.getTokens().get(0).getValue().equalsIgnoreCase("UPDATE")) { - return parseUpdate(lexer.getTokens()); - } - else if (lexer.getTokens().get(0).getValue().equalsIgnoreCase("DELETE")) { - return parseDelete(lexer.getTokens()); - } - else if (lexer.getTokens().get(0).getValue().equalsIgnoreCase("ALTER")) { - return parseAlterTable(lexer.getTokens()); - } - else { - try { - throw new ParseFailedException("Parse failed!--Unsupport SQL:" + lexer.getTokens().get(0).getValue()); + try { + // check if the input is a create table statement + if ((lexer.getTokens().get(0).getValue().equalsIgnoreCase("CREATE") && lexer.getTokens().get(1).getValue().equalsIgnoreCase("TABLE")) || + (lexer.getTokens().get(0).getValue().equalsIgnoreCase("CREATE") && lexer.getTokens().get(2).getValue().equalsIgnoreCase("TEMPORARY") && lexer.getTokens().get(3).getValue().equalsIgnoreCase("TABLE"))) { + return parseCreateTab(lexer.getTokens()); + } else if (lexer.getTokens().get(0).getValue().equalsIgnoreCase("INSERT")) { + return parseInsert(lexer.getTokens()); + } else if (lexer.getTokens().get(0).getValue().equalsIgnoreCase("DROP")) { + return parseDrop(lexer.getTokens()); + } else if (lexer.getTokens().get(0).getValue().equalsIgnoreCase("SELECT")) { + return parseSelect(lexer.getTokens()); + } else if (lexer.getTokens().get(0).getValue().equalsIgnoreCase("UPDATE")) { + return parseUpdate(lexer.getTokens()); + } else if (lexer.getTokens().get(0).getValue().equalsIgnoreCase("DELETE")) { + return parseDelete(lexer.getTokens()); + } else if (lexer.getTokens().get(0).getValue().equalsIgnoreCase("ALTER")) { + return parseAlterTable(lexer.getTokens()); + } else if ( + (lexer.getTokens().get(0).getValue().equalsIgnoreCase("CREATE") && lexer.getTokens().get(1).getValue().equalsIgnoreCase("OR") && lexer.getTokens().get(2).getValue().equalsIgnoreCase("REPLACE") && lexer.getTokens().get(3).getValue().equalsIgnoreCase("VIEW")) + || (lexer.getTokens().get(0).getValue().equalsIgnoreCase("CREATE") && lexer.getTokens().get(1).getValue().equalsIgnoreCase("VIEW")) + ) { + return parseCreateView(lexer.getTokens()); + } else { + try { + throw new ParseFailedException("Parse failed!--Unsupport SQL:" + lexer.getTokens().get(0).getValue()); + } catch (ParseFailedException e) { + e.printStackTrace(); + } + return null; } - catch (ParseFailedException e) { - e.printStackTrace(); - } - return null; } + catch (Exception e) { + e.printStackTrace(); + } + return null; } /** @@ -1210,5 +1217,68 @@ public class OracleParser { return root; } + /** + * Create view + * Grammar: CREATE [OR REPLACE] VIEW view_name [(column_name [, column_name]...)] AS SELECT_statement; + * Example: CREATE OR REPLACE VIEW employee_details (full_name, pay) + * AS SELECT first_name, last_name, salary FROM employees; + */ + private ASTNode parseCreateView(List parseTokens) { + List tokens = new ArrayList<>(); + ASTNode root = new ViewCreateNode(); + ASTNode currentNode = root; + for (int i = 0; i < parseTokens.size(); i++) { + if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("CREATE")) { + tokens.add(parseTokens.get(i)); + } + else if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("OR")) { + tokens.add(parseTokens.get(i)); + } + else if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("REPLACE")) { + tokens.add(parseTokens.get(i)); + } + else if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("VIEW")) { + tokens.add(parseTokens.get(i)); + root.setTokens(tokens); + } + else if (parseTokens.get(i).hasType(Token.TokenType.IDENTIFIER) && currentNode instanceof ViewCreateNode) { + tokens = new ArrayList<>(); + for (int j = i; j < parseTokens.size(); j++) { + if (parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && parseTokens.get(j).getValue().equalsIgnoreCase("AS")) { + i = j - 1; + break; + } + tokens.add(parseTokens.get(j)); + } + ASTNode childNode = new ViewNameNode(tokens); + currentNode.addChild(childNode); + currentNode = childNode; + } + else if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("AS")) { + tokens = new ArrayList<>(); + tokens.add(parseTokens.get(i)); + ASTNode childNode = new ViewTargetNode(tokens); + currentNode.addChild(childNode); + currentNode = childNode; + tokens = parseTokens.subList(i + 1, parseTokens.size()); + ASTNode selectRootNode = parseSelect(tokens); + currentNode.addChild(selectRootNode); + currentNode = selectRootNode.getDeepestChild(); + ASTNode viewEndNode = new ViewEndNode(); + currentNode.addChild(viewEndNode); + currentNode = viewEndNode; + break; + } + else { + try { + throw new ParseFailedException("Failed to parse:" + parseTokens.get(i)); + } + catch (ParseFailedException e) { + e.printStackTrace(); + } + } + } + return root; + } }