From f82a2acf79dd7d18dc68c3122b6a0d31708377c1 Mon Sep 17 00:00:00 2001 From: XudongXie Date: Sat, 14 Sep 2024 23:12:23 +0800 Subject: [PATCH] add Loop --- sqlTranslate/.idea/workspace.xml | 29 +++--- .../java/Generator/OpenGaussGenerator.java | 50 +++++++++- .../src/main/java/Lexer/OracleLexer.java | 9 +- sqlTranslate/src/main/java/Main.java | 29 ++++-- .../src/main/java/Parser/AST/ASTNode.java | 4 + .../main/java/Parser/AST/Loop/WhileNode.java | 2 +- .../src/main/java/Parser/OracleParser.java | 99 ++++++++++++++++++- 7 files changed, 193 insertions(+), 29 deletions(-) diff --git a/sqlTranslate/.idea/workspace.xml b/sqlTranslate/.idea/workspace.xml index 664b0258e..927c33fbd 100644 --- a/sqlTranslate/.idea/workspace.xml +++ b/sqlTranslate/.idea/workspace.xml @@ -4,7 +4,12 @@ - @@ -486,7 +490,8 @@ - diff --git a/sqlTranslate/src/main/java/Generator/OpenGaussGenerator.java b/sqlTranslate/src/main/java/Generator/OpenGaussGenerator.java index 031591ed1..e104de429 100644 --- a/sqlTranslate/src/main/java/Generator/OpenGaussGenerator.java +++ b/sqlTranslate/src/main/java/Generator/OpenGaussGenerator.java @@ -15,15 +15,18 @@ import Parser.AST.Drop.DropOptionNode; import Parser.AST.Insert.InsertNode; import Parser.AST.Join.JoinConditionNode; import Parser.AST.Join.JoinSourceTabNode; +import Parser.AST.Loop.ForNode; +import Parser.AST.Loop.LoopBodyNode; +import Parser.AST.Loop.LoopNode; +import Parser.AST.Loop.WhileNode; import Parser.AST.Select.SelectNode; -import Parser.AST.Select.SelectObjNode; import Parser.AST.Update.UpdateNode; -import Parser.AST.Update.UpdateObjNode; import Parser.AST.View.ViewCreateNode; -import Parser.OracleParser; import java.util.ArrayList; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class OpenGaussGenerator { private ASTNode node; @@ -63,6 +66,9 @@ public class OpenGaussGenerator { else if (node instanceof ViewCreateNode) { return GenCreateViewSQL(node); } + else if (node instanceof LoopNode || node instanceof WhileNode || node instanceof ForNode) { + return GenLoopSQL(node); + } else { try { throw new GenerateFailedException("Root node:" + node.getClass() + "(Unsupported node type!)"); @@ -123,6 +129,11 @@ public class OpenGaussGenerator { return node.toQueryString(); } + private String GenLoopSQL(ASTNode node) { + visitLoop(node); + return node.toQueryString(); + } + private void visitCrt(ASTNode node) { if (node instanceof ColumnNode) { ColumnTypeConvert((ColumnNode) node); @@ -162,6 +173,15 @@ public class OpenGaussGenerator { } } + private void visitLoop(ASTNode node) { + if (node instanceof LoopBodyNode) { + PLConvert(node); + } + for (ASTNode child : node.getChildren()) { + visitLoop(child); + } + } + private void visitSelect(ASTNode node) { if (node instanceof JoinSourceTabNode) { visitJoin(node); @@ -200,6 +220,30 @@ public class OpenGaussGenerator { visitCreateView(child); } } + private void PLConvert(ASTNode node) { + if (node.checkExistsByRegex("(?i)DBMS_OUTPUT.PUT_LINE\\(.*?\\)")) { + String printObj = ""; + for (Token token: node.getTokens()) { + if (token.getValue().matches("(?i)DBMS_OUTPUT.PUT_LINE\\(.*?\\)")) { + Pattern pattern = Pattern.compile("\\(([^()]*)\\)"); + Matcher matcher = pattern.matcher(token.getValue()); + while (matcher.find()) { + printObj = matcher.group(1); + } + break; + } + } + List tokens = new ArrayList<>(); + tokens.add(new Token(Token.TokenType.KEYWORD, "RAISE")); + tokens.add(new Token(Token.TokenType.KEYWORD, "NOTICE")); + tokens.add(new Token(Token.TokenType.STRING, "'%'")); + tokens.add(new Token(Token.TokenType.SYMBOL, ",")); + tokens.add(new Token(Token.TokenType.IDENTIFIER, printObj)); + tokens.add(new Token(Token.TokenType.SYMBOL, ";")); + node.setTokens(tokens); + } + } + private void ColumnTypeConvert(ColumnType node) { // type convert diff --git a/sqlTranslate/src/main/java/Lexer/OracleLexer.java b/sqlTranslate/src/main/java/Lexer/OracleLexer.java index cc443a949..fd5c8526b 100644 --- a/sqlTranslate/src/main/java/Lexer/OracleLexer.java +++ b/sqlTranslate/src/main/java/Lexer/OracleLexer.java @@ -30,7 +30,7 @@ public class OracleLexer { // keywords of view , "REPLACE", "VIEW" // PL/SQL - , ":=", "IF", "ELSIF", "BEGIN" + , ":=", "IF", "ELSIF", "BEGIN", ".." // Loop , "LOOP", "EXIT", "WHILE", "FOR", "IN" }; @@ -50,6 +50,7 @@ public class OracleLexer { "(MAX\\(.*?\\))|" + // MAX() function "(MIN\\(.*?\\))|" + // MIN() function "(\\|\\|\\s*'.*?'\\s*\\|\\|)|" + // || '*' || + "(DBMS_OUTPUT.PUT_LINE\\(.*?\\))|" + // DBMS_OUTPUT.PUT_LINE() function "(NOT NULL)|" + "(PRIMARY KEY)|" + @@ -73,7 +74,7 @@ public class OracleLexer { "(CROSS JOIN)|" + "(\\|\\|)|" + "(:=)|" + - + "(\\.\\.)|" + "(\\b[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*\\b)|" + // Keywords and identifiers "(\\b[A-Za-z_][A-Za-z0-9_]*\\b)|" + // Keywords and identifiers @@ -168,6 +169,9 @@ public class OracleLexer { } else if (tokenValue.matches("\\|\\|\\s*'.*?'\\s*\\|\\|")) { // RAW() function, CASE_INSENSITIVE return new Token(Token.TokenType.KEYWORD, tokenValue); + } else if (tokenValue.matches("(?i)DBMS_OUTPUT.PUT_LINE\\(.*?\\)")) { + // RAW() function, CASE_INSENSITIVE + return new Token(Token.TokenType.KEYWORD, tokenValue); } else if (isKeyword(tokenValue)) { @@ -194,6 +198,7 @@ public class OracleLexer { input = input.replace(" ", " "); } input = input.replace(" (", "("); + input = input.replace("..", " .. "); return input; } diff --git a/sqlTranslate/src/main/java/Main.java b/sqlTranslate/src/main/java/Main.java index 9a4cf148d..35a3658c4 100644 --- a/sqlTranslate/src/main/java/Main.java +++ b/sqlTranslate/src/main/java/Main.java @@ -4,6 +4,9 @@ import Lexer.OracleLexer; import Parser.AST.ASTNode; import Parser.OracleParser; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + public class Main { public static void main(String[] args) { @@ -33,20 +36,26 @@ public class Main { // 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 Using e.department_id = d.department_id;"; - String sql = "IF v_salary >= 100000 THEN\n" + - " v_bonus := v_salary * 0.1;\n" + - " ELSIF v_salary >= 50000 THEN\n" + - " v_bonus := v_salary * 0.08;\n" + - " ELSIF v_salary >= 30000 THEN\n" + - " v_bonus := v_salary * 0.05;\n" + - " ELSE\n" + - " v_bonus := v_salary * 0.03;\n" + - " END IF;"; +// String sql = "IF v_salary >= 100000 THEN\n" + +// " v_bonus := v_salary * 0.1;\n" + +// " ELSIF v_salary >= 50000 THEN\n" + +// " v_bonus := v_salary * 0.08;\n" + +// " ELSIF v_salary >= 30000 THEN\n" + +// " v_bonus := v_salary * 0.05;\n" + +// " ELSE\n" + +// " v_bonus := v_salary * 0.03;\n" + +// " END IF;"; + String sql = "LOOP\n" + + " DBMS_OUTPUT.PUT_LINE(v_counter);\n" + + " v_counter := v_counter + 1;\n" + + " EXIT WHEN v_counter > 10;\n" + + " END LOOP;"; OracleLexer lexer = new OracleLexer(sql); lexer.printTokens(); // OracleParser parser = new OracleParser(lexer); // ASTNode root = parser.parse(); - ASTNode root = OracleParser.parseIFELSE(lexer.getTokens()); + ASTNode root = OracleParser.parseLoop(lexer.getTokens()); +// ASTNode root = OracleParser.parseIFELSE(lexer.getTokens()); // ASTNode root = OracleParser.parseCaseWhen(lexer.getTokens()); // ASTNode root = OracleParser.parseJoin(lexer.getTokens()); System.out.println(root.toQueryString()); diff --git a/sqlTranslate/src/main/java/Parser/AST/ASTNode.java b/sqlTranslate/src/main/java/Parser/AST/ASTNode.java index bed4c61a7..431fab543 100644 --- a/sqlTranslate/src/main/java/Parser/AST/ASTNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/ASTNode.java @@ -31,6 +31,10 @@ public abstract class ASTNode { this.tokens = tokens; } + public boolean checkExistsByRegex (String regex) { + return tokens.stream().anyMatch(token -> token.getValue().matches(regex)); + } + public void addToken(Token token) { tokens.add(token); } diff --git a/sqlTranslate/src/main/java/Parser/AST/Loop/WhileNode.java b/sqlTranslate/src/main/java/Parser/AST/Loop/WhileNode.java index d4e4a775e..5574da7d3 100644 --- a/sqlTranslate/src/main/java/Parser/AST/Loop/WhileNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/Loop/WhileNode.java @@ -24,7 +24,7 @@ public class WhileNode extends ASTNode { @Override public void visit(ASTNode node, StringBuilder queryString) { - queryString.append(toString()); + queryString.append(toString() + " "); for (ASTNode child : getChildren()) { child.visit(child, queryString); diff --git a/sqlTranslate/src/main/java/Parser/OracleParser.java b/sqlTranslate/src/main/java/Parser/OracleParser.java index d83c3feaf..2f893ac34 100644 --- a/sqlTranslate/src/main/java/Parser/OracleParser.java +++ b/sqlTranslate/src/main/java/Parser/OracleParser.java @@ -24,6 +24,7 @@ import Parser.AST.Insert.InsertEndNode; import Parser.AST.Insert.InsertNode; import Parser.AST.Insert.InsertObjNode; import Parser.AST.Join.*; +import Parser.AST.Loop.*; import Parser.AST.Select.*; import Parser.AST.Update.*; import Parser.AST.View.ViewCreateNode; @@ -749,7 +750,103 @@ public class OracleParser { * @param parseTokens should start with LOOP | WHILE | FOR */ public static ASTNode parseLoop(List parseTokens) { - return null; + ASTNode root = null; + ASTNode currentNode = null; + for (int i = 0; i < parseTokens.size(); i++) { + //match LOOP + if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("LOOP")) { + root = new LoopNode(); + root.addToken(parseTokens.get(i)); + currentNode = root; + } + // match WHILE condition LOOP + else if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("WHILE")) { + root = new WhileNode(); + for (int j = i; j < parseTokens.size(); j++) { + root.addToken(parseTokens.get(j)); + if (parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && parseTokens.get(j).getValue().equals("LOOP")) { + i = j; + break; + } + } + currentNode = root; + + } + // match FOR counter IN start..stop LOOP + else if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("FOR")) { + root = new ForNode(); + for (int j = i; j < parseTokens.size(); j++) { + root.addToken(parseTokens.get(j)); + if (parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && parseTokens.get(j).getValue().equals("LOOP")) { + i = j; + break; + } + } + currentNode = root; + } + // match END LOOP; + else if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("END")) { + ASTNode childNode = new LoopEndNode(); + for (int j = i; j < parseTokens.size(); j++) { + childNode.addToken(parseTokens.get(j)); + if (parseTokens.get(j).hasType(Token.TokenType.SYMBOL) && parseTokens.get(j).getValue().equals(";")) { + i = j; + break; + } + } + currentNode.addChild(childNode); + currentNode = childNode; + } + // match EXIT WHEN condition; + else if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("EXIT")) { + ASTNode childNode = new LoopExitNode(); + for (int j = i; j < parseTokens.size(); j++) { + childNode.addToken(parseTokens.get(j)); + if (parseTokens.get(j).hasType(Token.TokenType.SYMBOL) && parseTokens.get(j).getValue().equals(";")) { + i = j; + break; + } + } + currentNode.addChild(childNode); + currentNode = childNode; + } + //match LOOP BODY + else if ( + (currentNode == root && root instanceof LoopNode) + || (currentNode == root && root instanceof WhileNode) + || (currentNode == root && root instanceof ForNode) + ) { + ASTNode childNode = new LoopBodyNode(); + for (int j = i; j < parseTokens.size(); j++) { + if ( + parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && parseTokens.get(j).getValue().equals("EXIT") + || parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && parseTokens.get(j).getValue().equals("END") + ) { + i = j - 1; + break; + } + childNode.addToken(parseTokens.get(j)); + if (parseTokens.get(j).hasType(Token.TokenType.SYMBOL) && parseTokens.get(j).getValue().equals(";")) { + currentNode.addChild(childNode); + currentNode = childNode; + childNode = new LoopBodyNode(); + } + } + } + else if (parseTokens.get(i).hasType(Token.TokenType.EOF)) { + break; + } + else { + try { + throw new ParseFailedException("Parse failed!--" + parseTokens.get(i)); + } + catch (ParseFailedException e) { + e.printStackTrace(); + } + } + } + + return root; } /**