add pl/sql

This commit is contained in:
XudongXie 2024-09-18 12:28:21 +08:00
parent ac03ad7130
commit 15ec96c822
7 changed files with 166 additions and 56 deletions

View File

@ -4,12 +4,13 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="d8d12963-4777-48e3-9669-309cf5e41d60" name="更改" comment="add PL ast classes">
<list default="true" id="d8d12963-4777-48e3-9669-309cf5e41d60" name="更改" comment="revise">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/Generator/OpenGaussGenerator.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/Generator/OpenGaussGenerator.java" 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/Lexer/Token.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/Lexer/Token.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/PL/PLDeclareNode.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/Parser/AST/PL/PLDeclareNode.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/Parser/AST/Procedure/ProcedureRetDefNode.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/Parser/AST/Procedure/ProcedureRetDefNode.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>
<option name="SHOW_DIALOG" value="false" />
@ -108,13 +109,6 @@
<option name="presentableId" value="Default" />
<updated>1723106789329</updated>
</task>
<task id="LOCAL-00043" summary="add select convert rules">
<created>1726148093221</created>
<option name="number" value="00043" />
<option name="presentableId" value="LOCAL-00043" />
<option name="project" value="LOCAL" />
<updated>1726148093221</updated>
</task>
<task id="LOCAL-00044" summary="add update ast class">
<created>1726149015586</created>
<option name="number" value="00044" />
@ -451,7 +445,14 @@
<option name="project" value="LOCAL" />
<updated>1726629819863</updated>
</task>
<option name="localTasksCounter" value="92" />
<task id="LOCAL-00092" summary="revise">
<created>1726631451338</created>
<option name="number" value="00092" />
<option name="presentableId" value="LOCAL-00092" />
<option name="project" value="LOCAL" />
<updated>1726631451338</updated>
</task>
<option name="localTasksCounter" value="93" />
<servers />
</component>
<component name="Vcs.Log.Tabs.Properties">
@ -477,7 +478,6 @@
<MESSAGE value="add Loop" />
<MESSAGE value="add Exception document" />
<MESSAGE value="add exception classes" />
<MESSAGE value="revise" />
<MESSAGE value="add exception" />
<MESSAGE value="add document of Procedure" />
<MESSAGE value="add ast classes of procedure" />
@ -491,7 +491,8 @@
<MESSAGE value="add trigger" />
<MESSAGE value="add PL document" />
<MESSAGE value="add PL ast classes" />
<option name="LAST_COMMIT_MESSAGE" value="add PL ast classes" />
<MESSAGE value="revise" />
<option name="LAST_COMMIT_MESSAGE" value="revise" />
</component>
<component name="XDebuggerManager">
<watches-manager>

View File

@ -26,6 +26,10 @@ import Parser.AST.Loop.ForNode;
import Parser.AST.Loop.LoopBodyNode;
import Parser.AST.Loop.LoopNode;
import Parser.AST.Loop.WhileNode;
import Parser.AST.PL.PLBodyNode;
import Parser.AST.PL.PLDeclareNode;
import Parser.AST.PL.PLEndNode;
import Parser.AST.PL.PLNode;
import Parser.AST.Procedure.ProcedureColumnNode;
import Parser.AST.Procedure.ProcedureEndNode;
import Parser.AST.Procedure.ProcedureNode;
@ -93,6 +97,9 @@ public class OpenGaussGenerator {
else if (node instanceof TriggerNode) {
return GenTriggerSQL(node);
}
else if (node instanceof PLNode) {
return GenPLSQL(node);
}
else {
try {
throw new GenerateFailedException("Root node:" + node.getClass() + "(Unsupported node type!)");
@ -179,9 +186,14 @@ public class OpenGaussGenerator {
return node.toQueryString();
}
private String GenPLSQL(ASTNode node) {
visitPLSQL(node);
return node.toQueryString();
}
private void visitCrt(ASTNode node) {
if (node instanceof ColumnNode) {
ColumnTypeConvert((ColumnNode) node);
DataTypeConvert((ColumnNode) node);
}
for (ASTNode child : node.getChildren()) {
visitCrt(child);
@ -256,10 +268,10 @@ public class OpenGaussGenerator {
private void visitAlter(ASTNode node) {
if (node instanceof AlterAddColumnNode) {
ColumnTypeConvert((AlterAddColumnNode) node);
DataTypeConvert((AlterAddColumnNode) node);
}
else if (node instanceof AlterModifyColumnNode) {
ColumnTypeConvert((AlterModifyColumnNode) node);
DataTypeConvert((AlterModifyColumnNode) node);
}
for (ASTNode child : node.getChildren()) {
visitAlter(child);
@ -277,11 +289,11 @@ public class OpenGaussGenerator {
private void visitPL(ASTNode node) {
if (node instanceof ProcedureRetDefNode) {
ColumnTypeConvert((ProcedureRetDefNode) node);
DataTypeConvert((ProcedureRetDefNode) node);
PLConvert(node);
}
else if (node instanceof ProcedureColumnNode) {
ColumnTypeConvert((ProcedureColumnNode) node);
DataTypeConvert((ProcedureColumnNode) node);
}
else if (node instanceof ProcedureEndNode) {
PLConvert(node);
@ -302,7 +314,7 @@ public class OpenGaussGenerator {
visitException(node);
}
else if (node instanceof FunctionColumnNode) {
ColumnTypeConvert((FunctionColumnNode) node);
DataTypeConvert((FunctionColumnNode) node);
}
else if (node instanceof FunctionEndNode) {
PLConvert(node);
@ -327,6 +339,24 @@ public class OpenGaussGenerator {
}
}
private void visitPLSQL(ASTNode node) {
if (node instanceof PLNode) {
PLConvert(node);
}
else if (node instanceof PLDeclareNode) {
DataTypeConvert((PLDeclareNode) node);
}
else if (node instanceof PLEndNode) {
PLConvert(node);
}
else if (node instanceof PLBodyNode) {
PLConvert(node);
}
for (ASTNode child : node.getChildren()) {
visitPLSQL(child);
}
}
private void PLConvert(ASTNode node) {
if (node.checkExistsByRegex("(?i)DBMS_OUTPUT.PUT_LINE\\(.*?\\)")) {
String printObj = "";
@ -460,17 +490,31 @@ public class OpenGaussGenerator {
}
}
}
if (node instanceof PLNode) {
List<Token> tokens = new ArrayList<>();
tokens.add(new Token(Token.TokenType.KEYWORD, "DO"));
tokens.add(new Token(Token.TokenType.KEYWORD, "$$"));
tokens.add(new Token(Token.TokenType.KEYWORD, "DECLARE"));
node.setTokens(tokens);
}
if (node instanceof PLEndNode) {
List<Token> tokens = new ArrayList<>();
tokens.add(new Token(Token.TokenType.KEYWORD, "END"));
tokens.add(new Token(Token.TokenType.KEYWORD, "$$"));
tokens.add(new Token(Token.TokenType.SYMBOL, ";"));
node.setTokens(tokens);
}
}
private void ColumnTypeConvert(DataType node) {
private void DataTypeConvert(DataType node) {
// type convert
if (node.getType().getValue().equalsIgnoreCase("NUMBER")) {
node.setType(new Token(Token.TokenType.KEYWORD, "NUMERIC"));
node.ResetTokensbyNameTypeConstraint();
}
if (node.getType().getValue().matches("(?i)NUMBER\\(.*?\\)")) {
node.setType(new Token(Token.TokenType.KEYWORD, node.getType().getValue().replace("NUMBER", "DECIMAL")));
node.setType(new Token(Token.TokenType.KEYWORD, node.getType().getValue().toUpperCase().replace("NUMBER", "DECIMAL")));
node.ResetTokensbyNameTypeConstraint();
}
@ -479,7 +523,7 @@ public class OpenGaussGenerator {
node.ResetTokensbyNameTypeConstraint();
}
if (node.getType().getValue().matches("(?i)VARCHAR2\\(.*?\\)")) {
node.setType(new Token(Token.TokenType.KEYWORD, node.getType().getValue().replace("VARCHAR2", "VARCHAR")));
node.setType(new Token(Token.TokenType.KEYWORD, node.getType().getValue().toUpperCase().replace("VARCHAR2", "VARCHAR")));
node.ResetTokensbyNameTypeConstraint();
}
@ -488,7 +532,7 @@ public class OpenGaussGenerator {
node.ResetTokensbyNameTypeConstraint();
}
if (node.getType().getValue().matches("(?i)RAW\\(.*?\\)")) {
node.setType(new Token(Token.TokenType.KEYWORD, node.getType().getValue().replace("RAW", "BYTEA")));
node.setType(new Token(Token.TokenType.KEYWORD, node.getType().getValue().toUpperCase().replace("RAW", "BYTEA")));
node.ResetTokensbyNameTypeConstraint();
}
@ -502,7 +546,7 @@ public class OpenGaussGenerator {
node.ResetTokensbyNameTypeConstraint();
}
if (node.getType().getValue().matches("(?i)NCHAR\\(.*?\\)")) {
node.setType(new Token(Token.TokenType.KEYWORD, node.getType().getValue().replace("NCHAR", "VARCHAR")));
node.setType(new Token(Token.TokenType.KEYWORD, node.getType().getValue().toUpperCase().replace("NCHAR", "VARCHAR")));
node.ResetTokensbyNameTypeConstraint();
}
@ -511,7 +555,7 @@ public class OpenGaussGenerator {
node.ResetTokensbyNameTypeConstraint();
}
if (node.getType().getValue().matches("(?i)NVARCHAR2\\(.*?\\)")) {
node.setType(new Token(Token.TokenType.KEYWORD, node.getType().getValue().replace("NVARCHAR2", "VARCHAR")));
node.setType(new Token(Token.TokenType.KEYWORD, node.getType().getValue().toUpperCase().replace("NVARCHAR2", "VARCHAR")));
node.ResetTokensbyNameTypeConstraint();
}

View File

@ -30,7 +30,7 @@ public class OracleLexer {
// keywords of view
, "REPLACE", "VIEW"
// PL/SQL
, ":=", "IF", "ELSIF", "BEGIN", ".."
, ":=", "IF", "ELSIF", "BEGIN", "..", "DECLARE"
// Loop
, "LOOP", "EXIT", "WHILE", "FOR", "IN", "CONTINUE"
// Exception

View File

@ -4,7 +4,7 @@ import java.util.Objects;
public class Token {
public enum TokenType {
KEYWORD, IDENTIFIER, NUMBER, OPERATOR, STRING, SYMBOL, EOF
KEYWORD, IDENTIFIER, NUMBER, OPERATOR, STRING, SYMBOL, NULL, EOF
}
private TokenType type;

View File

@ -60,33 +60,33 @@ public class Main {
// " DBMS_OUTPUT.PUT_LINE('Caught an exception: Invalid number');\n" +
// " WHEN OTHERS THEN\n" +
// " DBMS_OUTPUT.PUT_LINE('Caught an exception: ' || SQLERRM);";
String sql = "CREATE OR REPLACE PROCEDURE update_salary (\n" +
" employee_id IN NUMBER,\n" +
" new_salary IN OUT NUMBER\n" +
" ) IS\n" +
" v_employee NUMBER;\n" +
" BEGIN\n" +
" SELECT * INTO v_employee FROM employees WHERE employee_id = employee_id;\n" +
"\n" +
" IF new_salary < 3000 THEN\n" +
" new_salary := new_salary * 1.1;\n" +
" ELSE\n" +
" new_salary := new_salary * 1.05;\n" +
" END IF;\n" +
"\n" +
" UPDATE employees SET salary = new_salary WHERE employee_id = employee_id;\n" +
" COMMIT;\n" +
"\n" +
" DBMS_OUTPUT.PUT_LINE('Updated Salary: ' || new_salary);\n" +
" EXCEPTION\n" +
" WHEN NO_DATA_FOUND THEN\n" +
" DBMS_OUTPUT.PUT_LINE('Employee not found.');\n" +
" WHEN TOO_MANY_ROWS THEN\n" +
" DBMS_OUTPUT.PUT_LINE('Multiple employees found.');\n" +
" WHEN OTHERS THEN\n" +
" DBMS_OUTPUT.PUT_LINE('An error occurred: ' || SQLERRM);\n" +
" END update_salary;\n" +
" /";
// String sql = "CREATE OR REPLACE PROCEDURE update_salary (\n" +
// " employee_id IN NUMBER,\n" +
// " new_salary IN OUT NUMBER\n" +
// " ) IS\n" +
// " v_employee NUMBER;\n" +
// " BEGIN\n" +
// " SELECT * INTO v_employee FROM employees WHERE employee_id = employee_id;\n" +
// "\n" +
// " IF new_salary < 3000 THEN\n" +
// " new_salary := new_salary * 1.1;\n" +
// " ELSE\n" +
// " new_salary := new_salary * 1.05;\n" +
// " END IF;\n" +
// "\n" +
// " UPDATE employees SET salary = new_salary WHERE employee_id = employee_id;\n" +
// " COMMIT;\n" +
// "\n" +
// " DBMS_OUTPUT.PUT_LINE('Updated Salary: ' || new_salary);\n" +
// " EXCEPTION\n" +
// " WHEN NO_DATA_FOUND THEN\n" +
// " DBMS_OUTPUT.PUT_LINE('Employee not found.');\n" +
// " WHEN TOO_MANY_ROWS THEN\n" +
// " DBMS_OUTPUT.PUT_LINE('Multiple employees found.');\n" +
// " WHEN OTHERS THEN\n" +
// " DBMS_OUTPUT.PUT_LINE('An error occurred: ' || SQLERRM);\n" +
// " END update_salary;\n" +
// " /";
// String sql = "CREATE OR REPLACE FUNCTION string_length (\n" +
// " input_string VARCHAR2\n" +
// ") RETURN NUMBER IS\n" +
@ -100,6 +100,13 @@ public class Main {
// " BEGIN\n" +
// " INSERT INTO audit_log (action, employee_id) VALUES ('INSERT', :NEW.id);\n" +
// " END;";
String sql = "DECLARE\n" +
" v_name Varchar2(20);\n" +
" v_salary employees.salary%TYPE;\n" +
" BEGIN\n" +
" SELECT name, salary INTO v_name, v_salary FROM employees WHERE employee_id = 100;\n" +
" DBMS_OUTPUT.PUT_LINE('Name: ' || v_name || ', Salary: ' || v_salary);\n" +
" END;";
OracleLexer lexer = new OracleLexer(sql);
lexer.printTokens();
OracleParser parser = new OracleParser(lexer);

View File

@ -12,6 +12,7 @@ public class PLDeclareNode extends ASTNode implements DataType {
public PLDeclareNode() {
super();
setTokens(new ArrayList<>());
setType(new Token(Token.TokenType.NULL, ""));
}
public PLDeclareNode(ASTNode node) {

View File

@ -27,8 +27,7 @@ import Parser.AST.Insert.InsertNode;
import Parser.AST.Insert.InsertObjNode;
import Parser.AST.Join.*;
import Parser.AST.Loop.*;
import Parser.AST.PL.PLDeclareNode;
import Parser.AST.PL.PLNode;
import Parser.AST.PL.*;
import Parser.AST.Procedure.*;
import Parser.AST.Select.*;
import Parser.AST.Trigger.*;
@ -2369,13 +2368,71 @@ public class OracleParser {
i = j - 1;
break;
}
ASTNode childNode = new PLDeclareNode();
PLDeclareNode childNode = new PLDeclareNode();
for (int k = j; k < parseTokens.size(); k++) {
childNode.addToken(parseTokens.get(k));
if (parseTokens.get(k).hasType(Token.TokenType.KEYWORD)) {
childNode.setType(parseTokens.get(k));
}
if (parseTokens.get(k).hasType(Token.TokenType.SYMBOL) && parseTokens.get(k).getValue().equals(";")) {
j = k;
break;
}
}
currentNode.addChild(childNode);
currentNode = childNode;
}
}
// match begin
else if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("BEGIN")) {
ASTNode childNode = new PLBeginNode();
childNode.addToken(parseTokens.get(i));
currentNode.addChild(childNode);
currentNode = childNode;
}
// match body
else if (
(currentNode instanceof PLBeginNode || currentNode instanceof PLBodyNode) &&
!(parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("END"))
) {
ASTNode childNode = new PLBodyNode();
for (int j = i; j < parseTokens.size(); j++) {
if (parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && parseTokens.get(j).getValue().equalsIgnoreCase("END")) {
i = j - 1;
break;
}
else if (parseTokens.get(j).hasType(Token.TokenType.SYMBOL) && parseTokens.get(j).getValue().equals(";")) {
i = j;
childNode.addToken(parseTokens.get(j));
break;
}
childNode.addToken(parseTokens.get(j));
}
currentNode.addChild(childNode);
currentNode = childNode;
}
// match end
else if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("END")) {
ASTNode childNode = new PLEndNode();
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;
}
else if (parseTokens.get(i).hasType(Token.TokenType.EOF)) {
break;
}
else {
try {
throw new ParseFailedException("Failed to parse:" + parseTokens.get(i));
}
catch (ParseFailedException e) {
e.printStackTrace();
}
}
}