From bee259a0b4f78fe7606741106e6e2e03f538921b Mon Sep 17 00:00:00 2001 From: wenshao Date: Thu, 11 May 2017 21:09:58 +0800 Subject: [PATCH] improved oralce sql parser pl/sql support. --- .../SQLCreateProcedureStatement.java | 9 +- .../oracle/parser/OracleStatementParser.java | 37 +++++-- .../oracle/visitor/OracleOutputVisitor.java | 22 +++- .../sql/visitor/SQLASTOutputVisitor.java | 30 ++++-- .../bvt/sql/oracle/pl/Oracle_pl_for_0.java | 101 ++++++++++++++++++ 5 files changed, 180 insertions(+), 19 deletions(-) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/oracle/pl/Oracle_pl_for_0.java diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateProcedureStatement.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateProcedureStatement.java index 2b033f1186..3a823ffc9b 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateProcedureStatement.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateProcedureStatement.java @@ -28,6 +28,7 @@ public class SQLCreateProcedureStatement extends SQLStatementImpl { private SQLName definer; + private boolean create = true; private boolean orReplace; private SQLName name; private SQLStatement block; @@ -79,10 +80,16 @@ public class SQLCreateProcedureStatement extends SQLStatementImpl { public SQLName getDefiner() { return definer; } - public void setDefiner(SQLName definer) { this.definer = definer; } + public boolean isCreate() { + return create; + } + + public void setCreate(boolean create) { + this.create = create; + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java index cb67499401..198846f432 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java @@ -433,6 +433,12 @@ public class OracleStatementParser extends SQLStatementParser { continue; } + if (lexer.token() == Token.PROCEDURE) { + SQLStatement stmt = this.parseCreateProcedure(); + statementList.add(stmt); + continue; + } + if (lexer.token() == Token.ELSIF && parent instanceof SQLIfStatement) { break; } @@ -1091,6 +1097,11 @@ public class OracleStatementParser extends SQLStatementParser { parameter.setDefaultValue(new SQLQueryExpr(select)); } else { parameter.setName(this.exprParser.name()); + + if (lexer.token() == Token.IN) { + lexer.nextToken(); + parameter.setParamType(SQLParameter.ParameterType.IN); + } parameter.setDataType(this.exprParser.parseDataType()); if (lexer.token() == Token.COLONEQ) { @@ -1544,16 +1555,22 @@ public class OracleStatementParser extends SQLStatementParser { public SQLCreateProcedureStatement parseCreateProcedure() { SQLCreateProcedureStatement stmt = new SQLCreateProcedureStatement(); - accept(Token.CREATE); - if (lexer.token() == Token.OR) { + + if (lexer.token() == Token.CREATE) { lexer.nextToken(); - accept(Token.REPLACE); - stmt.setOrReplace(true); + if (lexer.token() == Token.OR) { + lexer.nextToken(); + accept(Token.REPLACE); + stmt.setOrReplace(true); + } + } else { + stmt.setCreate(false); } accept(Token.PROCEDURE); - stmt.setName(this.exprParser.name()); + SQLName procedureName = this.exprParser.name(); + stmt.setName(procedureName); if (lexer.token() == Token.LPAREN) { lexer.nextToken(); @@ -1561,12 +1578,20 @@ public class OracleStatementParser extends SQLStatementParser { accept(Token.RPAREN); } - accept(Token.AS); + if (lexer.token() == Token.IS) { + lexer.nextToken(); + } else { + accept(Token.AS); + } SQLBlockStatement block = this.parseBlock(); stmt.setBlock(block); + if (identifierEquals(procedureName.getSimpleName())) { + lexer.nextToken(); + } + return stmt; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java index 46c63ac3fe..0cd0428bab 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java @@ -1864,7 +1864,15 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS incrementIndent(); println(); - printlnAndAccept(x.getStatements(), ";"); + for (int i = 0, size = x.getStatements().size(); i < size; ++i) { + SQLStatement stmt = x.getStatements().get(i); + stmt.accept(this); + if (i != size - 1) { + println(";"); + } else { + print(';'); + } + } decrementIndent(); println(); @@ -2503,7 +2511,10 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS @Override public boolean visit(SQLCreateProcedureStatement x) { - if (x.isOrReplace()) { + boolean create = x.isCreate(); + if (!create) { + print0(ucase ? "PROCEDURE " : "procedure "); + } else if (x.isOrReplace()) { print0(ucase ? "CREATE OR REPLACE PROCEDURE " : "create or replace procedure "); } else { print0(ucase ? "CREATE PROCEDURE " : "create procedure "); @@ -2531,7 +2542,12 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS print(')'); } - println(); + if (!create) { + println(); + println("IS"); + } else { + println(); + } x.getBlock().setParent(x); x.getBlock().accept(this); return false; diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java index 0d97c0a8d1..efca84a919 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -3337,16 +3337,28 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet decrementIndent(); } else { - - if (x.getParamType() == SQLParameter.ParameterType.IN) { - print0(ucase ? "IN " : "in "); - } else if (x.getParamType() == SQLParameter.ParameterType.OUT) { - print0(ucase ? "OUT " : "out "); - } else if (x.getParamType() == SQLParameter.ParameterType.INOUT) { - print0(ucase ? "INOUT " : "inout "); + if (JdbcConstants.ORACLE.equals(dbType)) { + x.getName().accept(this); + if (x.getParamType() == SQLParameter.ParameterType.IN) { + print0(ucase ? " IN " : " in "); + } else if (x.getParamType() == SQLParameter.ParameterType.OUT) { + print0(ucase ? " OUT " : " out "); + } else if (x.getParamType() == SQLParameter.ParameterType.INOUT) { + print0(ucase ? " INOUT " : " inout "); + } else { + print(' '); + } + } else { + if (x.getParamType() == SQLParameter.ParameterType.IN) { + print0(ucase ? "IN " : "in "); + } else if (x.getParamType() == SQLParameter.ParameterType.OUT) { + print0(ucase ? "OUT " : "out "); + } else if (x.getParamType() == SQLParameter.ParameterType.INOUT) { + print0(ucase ? "INOUT " : "inout "); + } + x.getName().accept(this); + print(' '); } - x.getName().accept(this); - print(' '); x.getDataType().accept(this); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/pl/Oracle_pl_for_0.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/pl/Oracle_pl_for_0.java new file mode 100644 index 0000000000..4f6b328415 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/pl/Oracle_pl_for_0.java @@ -0,0 +1,101 @@ +/* + * Copyright 1999-2017 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.oracle.pl; + +import com.alibaba.druid.sql.OracleTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.visitor.SchemaStatVisitor; +import com.alibaba.druid.util.JdbcConstants; + +import java.util.List; + +public class Oracle_pl_for_0 extends OracleTest { + + public void test_0() throws Exception { + String sql = "PROCEDURE display_multiple_years (\n" + + " start_year_in IN PLS_INTEGER\n" + + " ,end_year_in IN PLS_INTEGER\n" + + ")\n" + + "IS\n" + + "BEGIN\n" + + " FOR l_current_year \n" + + " IN start_year_in .. end_year_in\n" + + " LOOP\n" + + " display_total_sales \n" + + " (l_current_year);\n" + + " END LOOP;\n" + + "END display_multiple_years;"; // + + List statementList = SQLUtils.parseStatements(sql, JdbcConstants.ORACLE); + SQLStatement stmt = statementList.get(0); + + assertEquals(1, statementList.size()); + + SchemaStatVisitor visitor = SQLUtils.createSchemaStatVisitor(JdbcConstants.ORACLE); + for (SQLStatement statement : statementList) { + statement.accept(visitor); + } + +// System.out.println("Tables : " + visitor.getTables()); +// System.out.println("fields : " + visitor.getColumns()); +// System.out.println("coditions : " + visitor.getConditions()); +// System.out.println("relationships : " + visitor.getRelationships()); +// System.out.println("orderBy : " + visitor.getOrderByColumns()); + + assertEquals(0, visitor.getTables().size()); + +// Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("employees"))); +// Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("emp_name"))); + +// Assert.assertEquals(7, visitor.getColumns().size()); +// Assert.assertEquals(3, visitor.getConditions().size()); +// Assert.assertEquals(1, visitor.getRelationships().size()); + + // Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("employees", "salary"))); + + { + String output = SQLUtils.toOracleString(stmt); + assertEquals("PROCEDURE display_multiple_years (\n" + + "\tstart_year_in IN PLS_INTEGER, \n" + + "\tend_year_in IN PLS_INTEGER\n" + + ")\n" + + "IS\n" + + "BEGIN\n" + + "\tFOR l_current_year IN start_year_in..end_year_in\n" + + "\tLOOP\n" + + "\t\tdisplay_total_sales(l_current_year);\n" + + "\tEND LOOP;\n" + + "END", // + output); + } + { + String output = SQLUtils.toOracleString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION); + assertEquals("procedure display_multiple_years (\n" + + "\tstart_year_in in PLS_INTEGER, \n" + + "\tend_year_in in PLS_INTEGER\n" + + ")\n" + + "IS\n" + + "begin\n" + + "\tfor l_current_year in start_year_in..end_year_in\n" + + "\tloop\n" + + "\t\tdisplay_total_sales(l_current_year);\n" + + "\tend loop;\n" + + "end", // + output); + } + } +}