diff --git a/source/core/annotations/ut_annotation_manager.pkb b/source/core/annotations/ut_annotation_manager.pkb index ceb085824..16d5657e5 100644 --- a/source/core/annotations/ut_annotation_manager.pkb +++ b/source/core/annotations/ut_annotation_manager.pkb @@ -79,7 +79,7 @@ create or replace package body ut_annotation_manager as where s.type = :a_object_type and s.owner = :a_object_owner and s.name - in (select /*+ cardinality( x ]'||l_card||q'[ )*/ + in (select /*+ cardinality( t ]'||l_card||q'[ )*/ x.name from table(:a_objects_to_refresh) t join ]'||l_sources_view||q'[ x diff --git a/source/core/annotations/ut_annotation_parser.pkb b/source/core/annotations/ut_annotation_parser.pkb index f35e6a0a7..d34956790 100644 --- a/source/core/annotations/ut_annotation_parser.pkb +++ b/source/core/annotations/ut_annotation_parser.pkb @@ -39,6 +39,43 @@ create or replace package body ut_annotation_parser as ,modifier => 'n'); end; + procedure add_annotation( + a_annotations in out nocopy ut_annotations, + a_position positiven, + a_comment varchar2, + a_subobject_name varchar2 := null + ) is + l_annotation_str varchar2(32767); + l_annotation_text varchar2(32767); + l_annotation_name varchar2(1000); + begin + -- strip everything except the annotation itself (spaces and others) + l_annotation_str := regexp_substr(a_comment, c_annotation_pattern, 1, 1, modifier => 'i'); + if l_annotation_str is not null then + + -- get the annotation name and it's parameters if present + l_annotation_name := lower(regexp_substr(l_annotation_str + ,'%(' || c_regexp_identifier || ')' + ,modifier => 'i' + ,subexpression => 1)); + l_annotation_text := trim(regexp_substr(l_annotation_str, '\((.*?)\)\s*$', subexpression => 1)); + + a_annotations.extend; + a_annotations( a_annotations.last) := + ut_annotation(a_position, l_annotation_name, l_annotation_text, a_subobject_name); + end if; + end; + + procedure delete_processed_comments( a_comments in out nocopy tt_comment_list, a_annotations ut_annotations ) is + l_loop_index pls_integer := 1; + begin + l_loop_index := a_annotations.first; + while l_loop_index is not null loop + a_comments.delete( a_annotations(l_loop_index).position ); + l_loop_index := a_annotations.next( l_loop_index ); + end loop; + end; + procedure add_annotations( a_annotations in out nocopy ut_annotations, a_source varchar2, @@ -47,10 +84,6 @@ create or replace package body ut_annotation_parser as ) is l_loop_index pls_integer := 1; l_annotation_index pls_integer; - l_comment varchar2(32767); - l_annotation_str varchar2(32767); - l_annotation_text varchar2(32767); - l_annotation_name varchar2(1000); begin -- loop while there are unprocessed comment blocks while 0 != nvl(regexp_instr(srcstr => a_source @@ -60,49 +93,14 @@ create or replace package body ut_annotation_parser as ,0) loop -- define index of the comment block and get it's content from cache - l_annotation_index := to_number(regexp_substr(a_source - ,c_comment_replacer_regex_ptrn - ,1 - ,l_loop_index - ,subexpression => 1)); - - l_comment := a_comments( l_annotation_index ); - - -- strip everything except the annotation itself (spaces and others) - l_annotation_str := regexp_substr(l_comment, c_annotation_pattern, 1, 1, modifier => 'i'); - if l_annotation_str is not null then - - -- get the annotation name and it's parameters if present - l_annotation_name := lower(regexp_substr(l_annotation_str - ,'%(' || c_regexp_identifier || ')' - ,modifier => 'i' - ,subexpression => 1)); - l_annotation_text := trim(regexp_substr(l_annotation_str, '\((.*?)\)\s*$', subexpression => 1)); - - a_annotations.extend; - a_annotations( a_annotations.last) := - ut_annotation(l_annotation_index, l_annotation_name, l_annotation_text, a_subobject_name); - end if; + l_annotation_index := regexp_substr( a_source ,c_comment_replacer_regex_ptrn ,1 ,l_loop_index ,subexpression => 1); + add_annotation( a_annotations, l_annotation_index, a_comments( l_annotation_index ), a_subobject_name ); l_loop_index := l_loop_index + 1; end loop; end add_annotations; - procedure add_package_annotations(a_annotations in out nocopy ut_annotations, a_source clob, a_comments tt_comment_list) is - l_package_comments varchar2(32767); - begin - l_package_comments := regexp_substr(srcstr => a_source - ,pattern => '^\s*(CREATE\s+(OR\s+REPLACE)?(\s+(NON)?EDITIONABLE)?\s+)?PACKAGE\s[^;]*?(\s+(AS|IS)\s+)((.*?{COMMENT#\d+}\s?)+)' - ,modifier => 'i' - ,subexpression => 7); - - -- parsing for package annotations - if l_package_comments is not null then - add_annotations(a_annotations, l_package_comments, a_comments); - end if; - end add_package_annotations; - - procedure add_procedure_annotations(a_annotations in out nocopy ut_annotations, a_source clob, a_comments tt_comment_list) is + procedure add_procedure_annotations(a_annotations in out nocopy ut_annotations, a_source clob, a_comments in out nocopy tt_comment_list) is l_proc_comments varchar2(32767); l_proc_name varchar2(250); l_annot_proc_ind number; @@ -119,7 +117,7 @@ create or replace package body ut_annotation_parser as ,position => l_annot_proc_ind); exit when l_annot_proc_ind = 0; - --get the annotataions with procedure name + --get the annotations with procedure name l_annot_proc_block := regexp_substr(srcstr => a_source ,pattern => c_annotation_block_pattern ,position => l_annot_proc_ind @@ -140,7 +138,6 @@ create or replace package body ut_annotation_parser as -- parse the comment block for the syntactically correct annotations and store them as an array add_annotations(a_annotations, l_proc_comments, a_comments, l_proc_name); - --l_annot_proc_ind := l_annot_proc_ind + length(l_annot_proc_block); l_annot_proc_ind := regexp_instr(srcstr => a_source ,pattern => ';' ,occurrence => 1 @@ -203,6 +200,7 @@ create or replace package body ut_annotation_parser as l_comments tt_comment_list; l_annotations ut_annotations := ut_annotations(); l_result ut_annotations; + l_comment_index positive; begin l_source := delete_multiline_comments(l_source); @@ -211,15 +209,20 @@ create or replace package body ut_annotation_parser as -- this call modifies l_source l_comments := extract_and_replace_comments(l_source); - add_package_annotations(l_annotations, l_source, l_comments); add_procedure_annotations(l_annotations, l_source, l_comments); + delete_processed_comments(l_comments, l_annotations); + + --at this point, only the comments not related to procedures are left, so we process them all as top-level + l_comment_index := l_comments.first; + while l_comment_index is not null loop + add_annotation( l_annotations, l_comment_index, l_comments( l_comment_index ) ); + l_comment_index := l_comments.next(l_comment_index); + end loop; dbms_lob.freetemporary(l_source); - select value(x) - bulk collect into l_result - from table(l_annotations) x - order by x.position; + + select value(x) bulk collect into l_result from table(l_annotations) x order by x.position; -- printing out parsed structure for debugging $if $$ut_trace $then diff --git a/test/ut_annotation_parser/test_annotation_parser.pkb b/test/ut_annotation_parser/test_annotation_parser.pkb index 903e4f542..f4d20c0af 100644 --- a/test/ut_annotation_parser/test_annotation_parser.pkb +++ b/test/ut_annotation_parser/test_annotation_parser.pkb @@ -26,13 +26,14 @@ create or replace package body test_annotation_parser is ut3.ut_annotation(1,'suite',null, null), ut3.ut_annotation(2,'displayname','Name of suite',null), ut3.ut_annotation(3,'suitepath','all.globaltests',null), + ut3.ut_annotation(4,'ann1','Name of suite',null), ut3.ut_annotation(5,'ann2','some_value','foo') ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; - procedure ignore_floating_annotations is + procedure include_floating_annotations is l_source clob; l_actual ut3.ut_annotations; l_expected ut3.ut_annotations; @@ -63,7 +64,11 @@ create or replace package body test_annotation_parser is ut3.ut_annotation( 1, 'suite', null, null ), ut3.ut_annotation( 2, 'displayname', 'Name of suite', null ), ut3.ut_annotation( 3, 'suitepath', 'all.globaltests', null ), + ut3.ut_annotation( 4, 'ann1', 'Name of suite', null ), + ut3.ut_annotation( 5, 'ann2', 'all.globaltests', null ), ut3.ut_annotation( 6, 'test', null, 'foo'), + ut3.ut_annotation( 7, 'ann3', 'Name of suite', null ), + ut3.ut_annotation( 8, 'ann4', 'all.globaltests', null ), ut3.ut_annotation( 9, 'test', null, 'bar') ); diff --git a/test/ut_annotation_parser/test_annotation_parser.pks b/test/ut_annotation_parser/test_annotation_parser.pks index b5ef9b5ed..84c201474 100644 --- a/test/ut_annotation_parser/test_annotation_parser.pks +++ b/test/ut_annotation_parser/test_annotation_parser.pks @@ -3,10 +3,10 @@ create or replace package test_annotation_parser is --%suite(ut_annotation_parser) --%suitepath(utplsql.core) - --%test(Ignores procedure level annotations if mixed with comments) + --%test(Treats procedure level annotations as package level, if mixed with comments) procedure test_proc_comments; - --%test(Ignores floating annotations between procedures and package) - procedure ignore_floating_annotations; + --%test(Includes floating annotations between procedures and package) + procedure include_floating_annotations; --%test(Parses complex annotations on procedures and functions) procedure parse_complex_with_functions; --%test(Parses package annotations without any procedure annotations)