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 3a823ffc9b..6a9cac2369 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 @@ -34,6 +34,9 @@ public class SQLCreateProcedureStatement extends SQLStatementImpl { private SQLStatement block; private List parameters = new ArrayList(); + // for oracle + private String javaCallSpec; + @Override public void accept0(SQLASTVisitor visitor) { if (visitor.visit(this)) { @@ -66,6 +69,9 @@ public class SQLCreateProcedureStatement extends SQLStatementImpl { } public void setBlock(SQLStatement block) { + if (block != null) { + block.setParent(this); + } this.block = block; } @@ -92,4 +98,12 @@ public class SQLCreateProcedureStatement extends SQLStatementImpl { public void setCreate(boolean create) { this.create = create; } + + public String getJavaCallSpec() { + return javaCallSpec; + } + + public void setJavaCallSpec(String javaCallSpec) { + this.javaCallSpec = javaCallSpec; + } } 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 1037effb41..81644e1982 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 @@ -18,12 +18,7 @@ package com.alibaba.druid.sql.dialect.oracle.parser; import java.util.ArrayList; import java.util.List; -import com.alibaba.druid.sql.ast.SQLDataTypeImpl; -import com.alibaba.druid.sql.ast.SQLExpr; -import com.alibaba.druid.sql.ast.SQLHint; -import com.alibaba.druid.sql.ast.SQLName; -import com.alibaba.druid.sql.ast.SQLParameter; -import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.*; import com.alibaba.druid.sql.ast.expr.*; import com.alibaba.druid.sql.ast.statement.*; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleReturningClause; @@ -1062,8 +1057,10 @@ public class OracleStatementParser extends SQLStatementParser { if (lexer.token() == Token.DECLARE) { lexer.nextToken(); + } - parserParameters(block.getParameters()); + if (lexer.token() == Token.IDENTIFIER) { + parserParameters(block.getParameters(), block); for (SQLParameter param : block.getParameters()) { param.setParent(block); } @@ -1078,7 +1075,7 @@ public class OracleStatementParser extends SQLStatementParser { return block; } - private void parserParameters(List parameters) { + private void parserParameters(List parameters, SQLObject parent) { for (;;) { SQLParameter parameter = new SQLParameter(); @@ -1591,7 +1588,7 @@ public class OracleStatementParser extends SQLStatementParser { if (lexer.token() == Token.LPAREN) { lexer.nextToken(); - parserParameters(stmt.getParameters()); + parserParameters(stmt.getParameters(), stmt); accept(Token.RPAREN); } @@ -1601,6 +1598,20 @@ public class OracleStatementParser extends SQLStatementParser { accept(Token.AS); } + if (identifierEquals("LANGUAGE")) { + lexer.nextToken(); + if (identifierEquals("JAVA")) { + lexer.nextToken(); + acceptIdentifier("NAME"); + String javaCallSpec = lexer.stringVal(); + accept(Token.LITERAL_CHARS); + stmt.setJavaCallSpec(javaCallSpec); + } else { + throw new ParserException("TODO : " + lexer.info()); + } + return stmt; + } + SQLBlockStatement block = this.parseBlock(); stmt.setBlock(block); 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 0cd0428bab..2abe816189 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 @@ -1355,9 +1355,11 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS @Override public boolean visit(SQLBlockStatement x) { if (x.getParameters().size() != 0) { - print0(ucase ? "DECLARE" : "declare"); incrementIndent(); - println(); + if (!(x.getParent() instanceof SQLCreateProcedureStatement)) { + print0(ucase ? "DECLARE" : "declare"); + println(); + } for (int i = 0, size = x.getParameters().size(); i < size; ++i) { if (i != 0) { @@ -2542,14 +2544,30 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS print(')'); } + SQLStatement block = x.getBlock(); + if (!create) { println(); println("IS"); } else { println(); + if (block instanceof SQLBlockStatement) { + SQLBlockStatement blockStatement = (SQLBlockStatement) block; + if (blockStatement.getParameters().size() > 0) { + println("AS"); + } + } } - x.getBlock().setParent(x); - x.getBlock().accept(this); + + String javaCallSpec = x.getJavaCallSpec(); + if (javaCallSpec != null) { + print0(ucase ? "LANGUAGE JAVA NAME '" : "language java name '"); + print0(javaCallSpec); + print('\''); + return false; + } + + block.accept(this); return false; } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java index 0b428fd7d6..b29e0168e8 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -1709,6 +1709,10 @@ public class SQLStatementParser extends SQLParser { return parseCreateView(); } else if (token == Token.TRIGGER) { return parseCreateTrigger(); + } else if (token == Token.PROCEDURE) { + SQLCreateProcedureStatement stmt = parseCreateProcedure(); + stmt.setCreate(true); + return stmt; } throw new ParserException("TODO " + lexer.token()); @@ -1791,7 +1795,7 @@ public class SQLStatementParser extends SQLParser { return stmt; } - public SQLStatement parseCreateProcedure() { + public SQLCreateProcedureStatement parseCreateProcedure() { throw new ParserException("TODO " + lexer.token()); } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/pl/Oracle_pl_0.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/pl/Oracle_pl_0.java new file mode 100644 index 0000000000..0687d2a678 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/pl/Oracle_pl_0.java @@ -0,0 +1,93 @@ +/* + * 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_0 extends OracleTest { + + public void test_0() throws Exception { + String sql = "CREATE PROCEDURE remove_emp (employee_id NUMBER) AS\n" + + " tot_emps NUMBER;\n" + + " BEGIN\n" + + " DELETE FROM employees\n" + + " WHERE employees.employee_id = remove_emp.employee_id;\n" + + " tot_emps := tot_emps - 1;\n" + + " END;"; // + + 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(1, 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("CREATE PROCEDURE remove_emp (\n" + + "\temployee_id NUMBER\n" + + ")\n" + + "AS\n" + + "tot_emps NUMBER;\n" + + "BEGIN\n" + + "\tDELETE FROM employees\n" + + "\tWHERE employees.employee_id = remove_emp.employee_id;\n" + + "\ttot_emps := tot_emps - 1;\n" + + "END", // + output); + } + { + String output = SQLUtils.toOracleString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION); + assertEquals("create procedure remove_emp (\n" + + "\temployee_id NUMBER\n" + + ")\n" + + "AS\n" + + "tot_emps NUMBER;\n" + + "begin\n" + + "\tdelete from employees\n" + + "\twhere employees.employee_id = remove_emp.employee_id;\n" + + "\ttot_emps := tot_emps - 1;\n" + + "end", // + output); + } + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/pl/Oracle_pl_external_0.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/pl/Oracle_pl_external_0.java new file mode 100644 index 0000000000..1a4a3f9f74 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/pl/Oracle_pl_external_0.java @@ -0,0 +1,81 @@ +/* + * 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_external_0 extends OracleTest { + + public void test_0() throws Exception { + String sql = "CREATE OR REPLACE PROCEDURE raise_salary (\n" + + " empid NUMBER,\n" + + " pct NUMBER\n" + + ") AS\n" + + " LANGUAGE JAVA NAME 'Adjuster.raiseSalary (int, float)'; -- call specification"; // + + 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("CREATE OR REPLACE PROCEDURE raise_salary (\n" + + "\tempid NUMBER, \n" + + "\tpct NUMBER\n" + + ")\n" + + "LANGUAGE JAVA NAME 'Adjuster.raiseSalary (int, float)'", // + output); + } + { + String output = SQLUtils.toOracleString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION); + assertEquals("create or replace procedure raise_salary (\n" + + "\tempid NUMBER, \n" + + "\tpct NUMBER\n" + + ")\n" + + "language java name 'Adjuster.raiseSalary (int, float)'", // + output); + } + } +}