diff --git a/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java b/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java index 88766f9d06..1ca73e37a7 100644 --- a/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java +++ b/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java @@ -2126,6 +2126,25 @@ protected void parserParameters(List parameters, SQLObject parent) } else { throw new ParserException("TODO : " + lexer.info()); } + } else if (lexer.identifierEquals(FnvHash.Constants.RECORD)) { + lexer.nextToken(); + + SQLRecordDataType recordDataType = new SQLRecordDataType(); + + accept(Token.LPAREN); + for (; ; ) { + SQLColumnDefinition column = this.exprParser.parseColumn(); + recordDataType.addColumn(column); + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + break; + } + accept(Token.RPAREN); + + dataType = recordDataType; + dataType.setDbType(dbType); } else { throw new ParserException("TODO : " + lexer.info()); } diff --git a/core/src/test/java/com/alibaba/druid/bvt/sql/oracle/issues/Issue6589.java b/core/src/test/java/com/alibaba/druid/bvt/sql/oracle/issues/Issue6589.java index d082e5b040..35dc91c0ad 100644 --- a/core/src/test/java/com/alibaba/druid/bvt/sql/oracle/issues/Issue6589.java +++ b/core/src/test/java/com/alibaba/druid/bvt/sql/oracle/issues/Issue6589.java @@ -9,39 +9,115 @@ import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** - * @see Issue 6589 - * Oracle PL/SQL parsing fails on TYPE ... IS TABLE OF inside package body. + * Fix Oracle PL/SQL TYPE ... IS RECORD(...) parsing in package body and declare blocks. + *

+ * The parser previously threw "TODO" when encountering RECORD after TYPE ... IS, + * only supporting REF CURSOR, TABLE OF, and VARRAY. + * + * @see Issue #6589 */ public class Issue6589 { @Test - public void test_type_is_table_of_in_package_body() { - String sql = "CREATE OR REPLACE PACKAGE BODY my_pkg AS\n" - + " type T_Validator_List is table of varchar2(200);\n" - + " PROCEDURE my_proc IS\n" - + " BEGIN\n" - + " NULL;\n" - + " END;\n" - + "END;"; + public void test_type_is_record_in_package_body_procedure() { + String sql = "create or replace PACKAGE BODY pkg_test IS\n" + + " procedure test_proc as\n" + + " type T_Rec is record(\n" + + " DeferredDate date,\n" + + " LicenseText varchar2(300));\n" + + " type T_Rec_List is table of T_Rec;\n" + + " v_items T_Rec_List;\n" + + " begin\n" + + " null;\n" + + " end test_proc;\n" + + "END pkg_test;"; SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, DbType.oracle); - List statementList = parser.parseStatementList(); - assertEquals(1, statementList.size()); + List stmtList = parser.parseStatementList(); + assertEquals(1, stmtList.size()); + assertNotNull(stmtList.get(0)); } @Test - public void test_type_is_table_of_index_by_in_package_body() { - String sql = "CREATE OR REPLACE PACKAGE BODY my_pkg AS\n" - + " type T_Name_List is table of varchar2(100) index by binary_integer;\n" - + " PROCEDURE my_proc IS\n" - + " BEGIN\n" - + " NULL;\n" - + " END;\n" + public void test_type_is_record_in_declare_block() { + String sql = "DECLARE\n" + + " type T_Rec is record(id number, name varchar2(100));\n" + + " v_rec T_Rec;\n" + + "BEGIN\n" + + " v_rec.id := 1;\n" + + " v_rec.name := 'test';\n" + "END;"; SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, DbType.oracle); - List statementList = parser.parseStatementList(); - assertEquals(1, statementList.size()); + List stmtList = parser.parseStatementList(); + assertEquals(1, stmtList.size()); + } + + @Test + public void test_type_is_record_multiple_fields() { + String sql = "create or replace PACKAGE BODY pkg_test IS\n" + + " procedure test_proc as\n" + + " type T_Employee is record(\n" + + " emp_id number,\n" + + " emp_name varchar2(200),\n" + + " hire_date date,\n" + + " salary number(10,2));\n" + + " begin\n" + + " null;\n" + + " end test_proc;\n" + + "END pkg_test;"; + + SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, DbType.oracle); + List stmtList = parser.parseStatementList(); + assertEquals(1, stmtList.size()); + } + + @Test + public void test_ref_cursor_still_works() { + String sql = "create or replace PACKAGE pkg_test AS\n" + + " TYPE t_cursor IS REF CURSOR;\n" + + " procedure get_data(p_result out t_cursor);\n" + + "END pkg_test;"; + + SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, DbType.oracle); + List stmtList = parser.parseStatementList(); + assertEquals(1, stmtList.size()); + } + + @Test + public void test_table_of_still_works() { + String sql = "create or replace PACKAGE BODY pkg_test IS\n" + + " TYPE t_list IS TABLE OF VARCHAR2(200);\n" + + " v_items t_list;\n" + + "END pkg_test;"; + + SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, DbType.oracle); + List stmtList = parser.parseStatementList(); + assertEquals(1, stmtList.size()); + } + + @Test + public void test_package_spec_and_body_with_record() { + String sql = "create or replace PACKAGE pkg_test AS\n" + + " TYPE t_cursor IS REF CURSOR;\n" + + " procedure test_proc(p_result out t_cursor);\n" + + "END pkg_test;\n" + + "/\n" + + "create or replace PACKAGE BODY pkg_test IS\n" + + " procedure test_proc(p_result out t_cursor) as\n" + + " type T_Rec is record(id number, name varchar2(100));\n" + + " begin\n" + + " null;\n" + + " end test_proc;\n" + + "END pkg_test;\n" + + "/"; + + SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, DbType.oracle); + List stmtList = parser.parseStatementList(); + assertNotNull(stmtList); + assertTrue(stmtList.size() >= 2); } }