This commit is contained in:
XudongXie 2024-09-12 15:55:26 +08:00
parent 34023bc8e8
commit 6fcb0541d0
7 changed files with 222 additions and 17 deletions

View File

@ -4,12 +4,13 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="d8d12963-4777-48e3-9669-309cf5e41d60" name="更改" comment="add join pattern"> <list default="true" id="d8d12963-4777-48e3-9669-309cf5e41d60" name="更改" comment="add join class">
<change afterPath="$PROJECT_DIR$/src/main/java/Parser/AST/Join/JoinColumnNode.java" afterDir="false" /> <change afterPath="$PROJECT_DIR$/src/main/java/Parser/AST/Join/JoinEndNode.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/Parser/AST/Join/JoinSourceTabNode.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/Parser/AST/Join/JoinTargetTabNode.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/Parser/AST/Join/JoinTypeNode.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/Lexer/OracleLexer.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/Lexer/OracleLexer.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/Main.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/Main.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/Parser/AST/Join/JoinColumnNode.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/Parser/AST/Join/JoinConditionNode.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/Parser/OracleParser.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/Parser/OracleParser.java" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -366,7 +367,14 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1726068883795</updated> <updated>1726068883795</updated>
</task> </task>
<option name="localTasksCounter" value="38" /> <task id="LOCAL-00038" summary="add join class">
<created>1726112198383</created>
<option name="number" value="00038" />
<option name="presentableId" value="LOCAL-00038" />
<option name="project" value="LOCAL" />
<updated>1726112198384</updated>
</task>
<option name="localTasksCounter" value="39" />
<servers /> <servers />
</component> </component>
<component name="Vcs.Log.Tabs.Properties"> <component name="Vcs.Log.Tabs.Properties">
@ -399,7 +407,8 @@
<MESSAGE value="add parseSelect" /> <MESSAGE value="add parseSelect" />
<MESSAGE value="revise" /> <MESSAGE value="revise" />
<MESSAGE value="add join pattern" /> <MESSAGE value="add join pattern" />
<option name="LAST_COMMIT_MESSAGE" value="add join pattern" /> <MESSAGE value="add join class" />
<option name="LAST_COMMIT_MESSAGE" value="add join class" />
</component> </component>
<component name="XDebuggerManager"> <component name="XDebuggerManager">
<watches-manager> <watches-manager>

View File

@ -20,7 +20,7 @@ public class OracleLexer {
// keywords of select // 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 // 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( private static final Pattern TOKEN_PATTERN = Pattern.compile(
"(NUMBER\\(.*?\\))|" + // NUMBER() function "(NUMBER\\(.*?\\))|" + // NUMBER() function

View File

@ -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 = "INSERT INTO employees (first_name, last_name, email) VALUES ('Jane', 'Smith', 'janesm@example.com');";
// String sql = "DROP TABLE employees CASCADE CONSTRAINTS;"; // 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 = "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); OracleLexer lexer = new OracleLexer(sql);
lexer.printTokens(); lexer.printTokens();
OracleParser parser = new OracleParser(lexer); OracleParser parser = new OracleParser(lexer);
ASTNode root = parser.parse(); ASTNode root = parser.parse();
// ASTNode root = OracleParser.parseJoin(lexer.getTokens());
System.out.println(root.toQueryString()); System.out.println(root.toQueryString());
System.out.println(root.getASTString()); System.out.println(root.getASTString());
OpenGaussGenerator generator = new OpenGaussGenerator(root); OpenGaussGenerator generator = new OpenGaussGenerator(root);

View File

@ -123,11 +123,13 @@ public abstract class ASTNode {
@Override @Override
public String toString() { public String toString() {
String str = ""; String str = "";
for (int i = 0; i < tokens.size(); i++) { if (tokens != null) {
if (i < tokens.size() - 1) for (int i = 0; i < tokens.size(); i++) {
str += tokens.get(i).getValue() + " "; if (i < tokens.size() - 1)
else str += tokens.get(i).getValue() + " ";
str += tokens.get(i).getValue(); else
str += tokens.get(i).getValue();
}
} }
return str; return str;
} }

View File

@ -4,20 +4,24 @@ import Lexer.Token;
import Parser.AST.ASTNode; import Parser.AST.ASTNode;
import java.util.List; import java.util.List;
public class JoinColumnNode extends ASTNode { public class JoinConditionNode extends ASTNode {
private String keyword; private String keyword;
public JoinColumnNode(List<Token> tokens) public JoinConditionNode(List<Token> tokens)
{ {
super(tokens); super(tokens);
this.keyword = ""; this.keyword = "";
} }
public JoinColumnNode(ASTNode node) public JoinConditionNode(ASTNode node)
{ {
super(node); super(node);
this.keyword = ""; this.keyword = "";
} }
public JoinConditionNode() {
super();
}
public void setKeyword(String keyword) { public void setKeyword(String keyword) {
this.keyword = keyword + " "; this.keyword = keyword + " ";
} }

View File

@ -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<Token> tokens) {
super(tokens);
}
public JoinEndNode(ASTNode node) {
super(node);
}
public JoinEndNode() {
super();
}
@Override
public void visit(ASTNode node, StringBuilder queryString) {
queryString.append(toString());
}
}

View File

@ -17,12 +17,12 @@ import Parser.AST.Insert.InsertDataNode;
import Parser.AST.Insert.InsertEndNode; import Parser.AST.Insert.InsertEndNode;
import Parser.AST.Insert.InsertNode; import Parser.AST.Insert.InsertNode;
import Parser.AST.Insert.InsertObjNode; import Parser.AST.Insert.InsertObjNode;
import Parser.AST.Join.*;
import Parser.AST.Select.*; import Parser.AST.Select.*;
import java.util.Stack; import java.util.Stack;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
public class OracleParser { public class OracleParser {
@ -581,5 +581,168 @@ public class OracleParser {
return root; 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<Token> parseTokens) {
List <Token> 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;
}
} }