diff --git a/sqlTranslate/.idea/workspace.xml b/sqlTranslate/.idea/workspace.xml
index c80ee9da3..4d84cba69 100644
--- a/sqlTranslate/.idea/workspace.xml
+++ b/sqlTranslate/.idea/workspace.xml
@@ -4,12 +4,13 @@
-
-
-
-
-
+
+
+
+
+
+
@@ -366,7 +367,14 @@
1726068883795
-
+
+ 1726112198383
+
+
+
+ 1726112198384
+
+
@@ -399,7 +407,8 @@
-
+
+
diff --git a/sqlTranslate/src/main/java/Lexer/OracleLexer.java b/sqlTranslate/src/main/java/Lexer/OracleLexer.java
index 8d958c5ee..90b2554c3 100644
--- a/sqlTranslate/src/main/java/Lexer/OracleLexer.java
+++ b/sqlTranslate/src/main/java/Lexer/OracleLexer.java
@@ -20,7 +20,7 @@ public class OracleLexer {
// keywords of select
, "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"
+ , "INNER JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN", "FULL JOIN", "FULL OUTER JOIN", "CROSS JOIN", "JOIN", "ON", "USING"
};
private static final Pattern TOKEN_PATTERN = Pattern.compile(
"(NUMBER\\(.*?\\))|" + // NUMBER() function
diff --git a/sqlTranslate/src/main/java/Main.java b/sqlTranslate/src/main/java/Main.java
index 470998c86..86a947bf6 100644
--- a/sqlTranslate/src/main/java/Main.java
+++ b/sqlTranslate/src/main/java/Main.java
@@ -17,10 +17,13 @@ public class Main {
// String sql = "INSERT INTO employees (first_name, last_name, email) VALUES ('Jane', 'Smith', 'janesm@example.com');";
// String sql = "DROP TABLE employees CASCADE CONSTRAINTS;";
String sql = "SELECT e.first_name, d.department_name FROM employees e JOIN departments d ON e.department_id = d.department_id;";
+// String sql = "Customers\n" +
+// "LEFT JOIN Orders ON Customers.CustomerID = Orders.CustomerID where";
OracleLexer lexer = new OracleLexer(sql);
lexer.printTokens();
OracleParser parser = new OracleParser(lexer);
ASTNode root = parser.parse();
+// ASTNode root = OracleParser.parseJoin(lexer.getTokens());
System.out.println(root.toQueryString());
System.out.println(root.getASTString());
OpenGaussGenerator generator = new OpenGaussGenerator(root);
diff --git a/sqlTranslate/src/main/java/Parser/AST/ASTNode.java b/sqlTranslate/src/main/java/Parser/AST/ASTNode.java
index 87272d962..14d5b11ea 100644
--- a/sqlTranslate/src/main/java/Parser/AST/ASTNode.java
+++ b/sqlTranslate/src/main/java/Parser/AST/ASTNode.java
@@ -123,11 +123,13 @@ public abstract class ASTNode {
@Override
public String toString() {
String str = "";
- for (int i = 0; i < tokens.size(); i++) {
- if (i < tokens.size() - 1)
- str += tokens.get(i).getValue() + " ";
- else
- str += tokens.get(i).getValue();
+ if (tokens != null) {
+ for (int i = 0; i < tokens.size(); i++) {
+ if (i < tokens.size() - 1)
+ str += tokens.get(i).getValue() + " ";
+ else
+ str += tokens.get(i).getValue();
+ }
}
return str;
}
diff --git a/sqlTranslate/src/main/java/Parser/AST/Join/JoinColumnNode.java b/sqlTranslate/src/main/java/Parser/AST/Join/JoinConditionNode.java
similarity index 75%
rename from sqlTranslate/src/main/java/Parser/AST/Join/JoinColumnNode.java
rename to sqlTranslate/src/main/java/Parser/AST/Join/JoinConditionNode.java
index e0a59f5eb..b2bfea11e 100644
--- a/sqlTranslate/src/main/java/Parser/AST/Join/JoinColumnNode.java
+++ b/sqlTranslate/src/main/java/Parser/AST/Join/JoinConditionNode.java
@@ -4,20 +4,24 @@ import Lexer.Token;
import Parser.AST.ASTNode;
import java.util.List;
-public class JoinColumnNode extends ASTNode {
+public class JoinConditionNode extends ASTNode {
private String keyword;
- public JoinColumnNode(List tokens)
+ public JoinConditionNode(List tokens)
{
super(tokens);
this.keyword = "";
}
- public JoinColumnNode(ASTNode node)
+ public JoinConditionNode(ASTNode node)
{
super(node);
this.keyword = "";
}
+ public JoinConditionNode() {
+ super();
+ }
+
public void setKeyword(String keyword) {
this.keyword = keyword + " ";
}
diff --git a/sqlTranslate/src/main/java/Parser/AST/Join/JoinEndNode.java b/sqlTranslate/src/main/java/Parser/AST/Join/JoinEndNode.java
new file mode 100644
index 000000000..53ca8eda5
--- /dev/null
+++ b/sqlTranslate/src/main/java/Parser/AST/Join/JoinEndNode.java
@@ -0,0 +1,24 @@
+package Parser.AST.Join;
+
+import Lexer.Token;
+import Parser.AST.ASTNode;
+import java.util.List;
+
+public class JoinEndNode extends ASTNode {
+ public JoinEndNode(List tokens) {
+ super(tokens);
+ }
+
+ public JoinEndNode(ASTNode node) {
+ super(node);
+ }
+
+ public JoinEndNode() {
+ super();
+ }
+
+ @Override
+ public void visit(ASTNode node, StringBuilder queryString) {
+ queryString.append(toString());
+ }
+}
diff --git a/sqlTranslate/src/main/java/Parser/OracleParser.java b/sqlTranslate/src/main/java/Parser/OracleParser.java
index 150b3881c..68a78a065 100644
--- a/sqlTranslate/src/main/java/Parser/OracleParser.java
+++ b/sqlTranslate/src/main/java/Parser/OracleParser.java
@@ -17,12 +17,12 @@ import Parser.AST.Insert.InsertDataNode;
import Parser.AST.Insert.InsertEndNode;
import Parser.AST.Insert.InsertNode;
import Parser.AST.Insert.InsertObjNode;
+import Parser.AST.Join.*;
import Parser.AST.Select.*;
import java.util.Stack;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
public class OracleParser {
@@ -581,5 +581,168 @@ public class OracleParser {
return root;
}
+ /**
+ * JOIN clause
+ * For example: table1 t1 JOIN table2 t2 ON|USING t1.id = t2.id (parseTokens should start with table)
+ */
+ public static ASTNode parseJoin(List parseTokens) {
+ List tokens = new ArrayList<>();
+ int index = 0;
+ boolean parseState = false;
+ ASTNode currentNode = null;
+ ASTNode root = null;
+ // match table 1
+ for (int i = index; i < parseTokens.size(); i++) {
+ if (
+ (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("JOIN"))
+ || (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("INNER JOIN"))
+ || (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("LEFT JOIN"))
+ || (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("RIGHT JOIN"))
+ || (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("FULL JOIN"))
+ || (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("LEFT OUTER JOIN"))
+ || (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("RIGHT OUTER JOIN"))
+ || (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("FULL OUTER JOIN"))
+ || (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("CROSS JOIN"))
+ ) {
+ index = i;
+ parseState = true;
+ break;
+ }
+ tokens.add(parseTokens.get(i));
+ }
+ if (parseState) {
+ root = new JoinSourceTabNode(tokens);
+ currentNode = root;
+ }
+ else {
+ try {
+ throw new ParseFailedException("Fail to parse:" + parseTokens.get(index));
+ }
+ catch (ParseFailedException e) {
+ e.printStackTrace();
+ }
+ }
+ // match join type
+ if (parseState) {
+ tokens = new ArrayList<>();
+ tokens.add(parseTokens.get(index));
+ index++;
+ ASTNode childNode = new JoinTypeNode(tokens);
+ currentNode.addChild(childNode);
+ currentNode = childNode;
+ }
+ else {
+ try {
+ throw new ParseFailedException("Fail to parse:" + parseTokens.get(index));
+ }
+ catch (ParseFailedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // match table 2
+ tokens = new ArrayList<>();
+ parseState = false;
+ for (int i = index; i < parseTokens.size(); i++) {
+ if (
+ (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("ON"))
+ || (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("USING"))
+ || (parseTokens.get(i).hasType(Token.TokenType.SYMBOL) && parseTokens.get(i).getValue().equalsIgnoreCase(";"))
+ || (parseTokens.get(i).hasType(Token.TokenType.KEYWORD))
+ ) {
+ index = i;
+ parseState = true;
+ break;
+ }
+ tokens.add(parseTokens.get(i));
+ }
+ if (parseState) {
+ ASTNode childNode = new JoinTargetTabNode(tokens);
+ currentNode.addChild(childNode);
+ currentNode = childNode;
+ }
+ else {
+ try {
+ throw new ParseFailedException("Fail to parse:" + parseTokens.get(index));
+ }
+ catch (ParseFailedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // match join condition
+ if (parseState) {
+ // match end
+ if (parseTokens.get(index).hasType(Token.TokenType.SYMBOL) && parseTokens.get(index).getValue().equalsIgnoreCase(";")) {
+ tokens = new ArrayList<>();
+ tokens.add(parseTokens.get(index));
+ ASTNode childNode = new JoinEndNode(tokens);
+ currentNode.addChild(childNode);
+ currentNode = childNode;
+ }
+ // match end with keyword
+ else if (parseTokens.get(index).hasType(Token.TokenType.KEYWORD) && !parseTokens.get(index).getValue().equalsIgnoreCase("ON") && !parseTokens.get(index).getValue().equalsIgnoreCase("USING")) {
+ ASTNode childNode = new JoinEndNode();
+ currentNode.addChild(childNode);
+ currentNode = childNode;
+ }
+ else {
+ parseState = false;
+ JoinConditionNode joinConditionNode = new JoinConditionNode();
+ joinConditionNode.setKeyword(parseTokens.get(index).getValue());
+ index++;
+ tokens = new ArrayList<>();
+ for (int i = index; i < parseTokens.size(); i++) {
+ if (
+ parseTokens.get(i).hasType(Token.TokenType.KEYWORD)
+ || (parseTokens.get(i).hasType(Token.TokenType.SYMBOL) && parseTokens.get(i).getValue().equalsIgnoreCase(";"))
+ )
+ {
+ index = i;
+ parseState = true;
+ break;
+ }
+ tokens.add(parseTokens.get(i));
+ }
+ if (parseState) {
+ joinConditionNode.setTokens(tokens);
+ currentNode.addChild(joinConditionNode);
+ currentNode = joinConditionNode;
+ // match end
+ if (parseTokens.get(index).hasType(Token.TokenType.SYMBOL) && parseTokens.get(index).getValue().equalsIgnoreCase(";")) {
+ tokens = new ArrayList<>();
+ tokens.add(parseTokens.get(index));
+ ASTNode childNode = new JoinEndNode(tokens);
+ currentNode.addChild(childNode);
+ currentNode = childNode;
+ }
+ // match end with keyword
+ else if (parseTokens.get(index).hasType(Token.TokenType.KEYWORD)) {
+ ASTNode childNode = new JoinEndNode();
+ currentNode.addChild(childNode);
+ currentNode = childNode;
+ }
+ }
+ else {
+ try {
+ throw new ParseFailedException("Fail to parse:" + parseTokens.get(index));
+ }
+ catch (ParseFailedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ else {
+ try {
+ throw new ParseFailedException("Fail to parse:" + parseTokens.get(index));
+ }
+ catch (ParseFailedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return root;
+ }
}