Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
38e02ab
Testing something
lwasylow Mar 9, 2026
461ad5d
Another test
lwasylow Mar 9, 2026
44f5495
Test
lwasylow Mar 9, 2026
b5f2dc7
Thats funny:)
lwasylow Mar 9, 2026
0dd7dba
that is rubbish
lwasylow Mar 9, 2026
4ea0fcb
revert test
lwasylow Mar 9, 2026
17604d8
Small tweaks
lwasylow Mar 9, 2026
5869f32
one at the time
lwasylow Mar 9, 2026
3f8d2cf
Move out outside
lwasylow Mar 9, 2026
37fae24
fix invalid locator
lwasylow Mar 9, 2026
5189955
revert
lwasylow Mar 9, 2026
5a21774
Changing a code to validate by lines instead of clobs for performance.
lwasylow Mar 14, 2026
39f5d64
Fixing loop syntax
lwasylow Mar 14, 2026
93ffb72
Merge branch 'develop' of https://github.com/utPLSQL/utPLSQL into fea…
lwasylow Mar 14, 2026
393cfec
Update code
lwasylow Mar 15, 2026
8d794f0
Update block
lwasylow Mar 15, 2026
a32e09d
Small fixes
lwasylow Mar 15, 2026
b2e914f
Update comment
lwasylow Mar 15, 2026
66de806
Introduce global variable
lwasylow Mar 15, 2026
f82a3b5
Cleanup
lwasylow Mar 15, 2026
934df67
Optimization fiurther
lwasylow Mar 15, 2026
65145c2
Trim spaces
lwasylow Mar 15, 2026
8105179
Added extra tests.
lwasylow Mar 15, 2026
cc491cf
Enhance annotation parser with new tests and source line handling fun…
lwasylow Mar 15, 2026
6717ad3
Add tests for Windows-style newlines and long procedure names in anno…
lwasylow Mar 15, 2026
6c513be
Fix regex extraction for procedure/function names and ensure ordered …
lwasylow Mar 15, 2026
46f3d12
Update tests
lwasylow Mar 15, 2026
81e1752
Refactor annotation parser tests and utility functions
lwasylow Mar 16, 2026
fecbcc1
Remove redundant parse_object_annotations function overloads and upda…
lwasylow Mar 16, 2026
2547b40
Update code to avoid
lwasylow Mar 17, 2026
40eb166
Enhance annotation processing by improving SQL text handling and addi…
lwasylow Mar 17, 2026
1cdeb9c
Refactor annotation processing and enhance line scanning functionalit…
lwasylow Mar 18, 2026
c4d0ab0
Remove redundant exception handling in build_annot_cache_for_sources …
lwasylow Mar 18, 2026
aa3bfe7
Refactor annotation patterns in ut_annotation_parser and remove unuse…
lwasylow Mar 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Enhance annotation processing by improving SQL text handling and addi…
…ng header stripping functionality
  • Loading branch information
lwasylow committed Mar 17, 2026
commit 40eb166c1a228b7d2ced30a471c8dd41335b8cb7
24 changes: 13 additions & 11 deletions source/core/annotations/ut_annotation_manager.pkb
Original file line number Diff line number Diff line change
Expand Up @@ -231,23 +231,25 @@

function get_source_from_sql_text(a_object_name varchar2, a_sql_text ora_name_list_t, a_parts binary_integer) return sys_refcursor is
l_sql_clob clob;
l_sql_lines ut_varchar2_rows := ut_varchar2_rows();
l_sql_lines dbms_preprocessor.source_lines_t := dbms_preprocessor.source_lines_t();
l_sql_lines_clob ut_varchar2_list := ut_varchar2_list();
l_result sys_refcursor;
l_replaced boolean := false;

Check warning on line 237 in source/core/annotations/ut_annotation_manager.pkb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unused local variable "l_replaced".

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZz94vO6oGBupAFrwqRP&open=AZz94vO6oGBupAFrwqRP&pullRequest=1312
begin
if a_parts > 0 then
for i in 1..a_parts loop
ut_utils.append_to_clob(l_sql_clob, a_sql_text(i));
end loop;
l_sql_clob := ut_utils.replace_multiline_comments(l_sql_clob);
-- replace comment lines that contain "-- create or replace"
l_sql_clob := regexp_replace(l_sql_clob, '^.*[-]{2,}\s*create(\s+or\s+replace).*$', modifier => 'mi');
-- remove the "create [or replace] [[non]editionable] " so that we have only "type|package|procedure|function" for parsing
-- needed for dbms_preprocessor
l_sql_clob := regexp_replace(l_sql_clob, '^(.*?\s*create(\s+or\s+replace)?(\s+(editionable|noneditionable))?\s+?)((package|type|procedure|function).*)', '\5', 1, 1, 'ni');
-- remove "OWNER." from create or replace statement.
-- Owner is not supported along with AUTHID - see issue https://github.com/utPLSQL/utPLSQL/issues/1088
l_sql_clob := regexp_replace(l_sql_clob, '^(package|type|procedure|function)\s+("?[[:alpha:]][[:alnum:]$#_]*"?\.)(.*)', '\1 \3', 1, 1, 'ni');
l_sql_lines := ut_utils.convert_collection( ut_utils.clob_to_table(l_sql_clob) );

l_sql_lines_clob := ut_utils.clob_to_table(l_sql_clob);
for i in 1..l_sql_lines_clob.count loop

Check failure on line 245 in source/core/annotations/ut_annotation_manager.pkb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this FOR loop by a more robust WHILE one.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZz94vO6oGBupAFrwqRO&open=AZz94vO6oGBupAFrwqRO&pullRequest=1312
l_sql_lines(i) := l_sql_lines_clob(i);
end loop;

-- replace multiline comments that contain "-- create or replace" with single line comment to avoid parsing issues with dbms_preprocessor
l_sql_lines := ut_utils.replace_multiline_comments(l_sql_lines);
-- strip CREATE header (possibly split across lines) while preserving line numbers
l_sql_lines := ut_utils.strip_create_header_lines(l_sql_lines);
end if;
open l_result for
select /*+ no_parallel */ a_object_name as name, column_value||chr(10) as text from table(l_sql_lines);
Expand Down
132 changes: 50 additions & 82 deletions source/core/ut_utils.pkb
Original file line number Diff line number Diff line change
Expand Up @@ -682,10 +682,10 @@
end if;

-- Fast pre-scan to check for presence of multi-line comments; if none, return original source unmodified
for i in 1 .. a_source.count loop

Check failure on line 685 in source/core/ut_utils.pkb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this FOR loop by a more robust WHILE one.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZzvRFHIwAnQ9xtGVyea&open=AZzvRFHIwAnQ9xtGVyea&pullRequest=1312
if instr(a_source(i), '/*') > 0 then
l_has_ml_comment := true;
exit;

Check warning on line 688 in source/core/ut_utils.pkb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Avoid using EXIT from within a FOR or WHILE loop.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZzvRFHIwAnQ9xtGVyeb&open=AZzvRFHIwAnQ9xtGVyeb&pullRequest=1312
end if;
end loop;

Expand All @@ -693,7 +693,7 @@
return a_source;
end if;

for i in 1 .. a_source.count loop

Check failure on line 696 in source/core/ut_utils.pkb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this FOR loop by a more robust WHILE one.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZzvRFHIwAnQ9xtGVyec&open=AZzvRFHIwAnQ9xtGVyec&pullRequest=1312
l_line := a_source(i);

-- Fast path: inside multi-line comment
Expand Down Expand Up @@ -723,8 +723,8 @@
l_line := null;

<<scan_line>>
Comment thread
lwasylow marked this conversation as resolved.
Outdated
loop

Check failure on line 726 in source/core/ut_utils.pkb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Simple loops of the form LOOP .. END LOOP; should be avoid as they behave by default as infinite loops.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZzvRFHIwAnQ9xtGVyed&open=AZzvRFHIwAnQ9xtGVyed&pullRequest=1312
exit when l_remaining is null or l_remaining = '';

Check warning on line 727 in source/core/ut_utils.pkb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This EXIT statement should explicitly mention the loop label.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZzvRFHIwAnQ9xtGVyee&open=AZzvRFHIwAnQ9xtGVyee&pullRequest=1312

Check warning on line 727 in source/core/ut_utils.pkb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use IS NULL and IS NOT NULL instead of direct NULL comparisons.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZzvRFHIwAnQ9xtGVyef&open=AZzvRFHIwAnQ9xtGVyef&pullRequest=1312
Comment thread
lwasylow marked this conversation as resolved.
Outdated
l_ml_start := instr(l_remaining, '/*');
l_comment_start := instr(l_remaining, '--');
l_text_start := instr(l_remaining, '''');
Expand Down Expand Up @@ -785,13 +785,13 @@
-- Regular string literal: l_remaining starts at the opening quote
-- scan from position 2 to skip the opening quote
l_end := 2;
loop

Check failure on line 788 in source/core/ut_utils.pkb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Simple loops of the form LOOP .. END LOOP; should be avoid as they behave by default as infinite loops.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZzvRFHIwAnQ9xtGVyeg&open=AZzvRFHIwAnQ9xtGVyeg&pullRequest=1312
l_end := instr(l_remaining, '''', l_end);
exit when l_end = 0;

Check warning on line 790 in source/core/ut_utils.pkb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This EXIT statement should explicitly mention the loop label.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZzvRFHIwAnQ9xtGVyeh&open=AZzvRFHIwAnQ9xtGVyeh&pullRequest=1312
if substr(l_remaining, l_end, 2) = '''''' then
l_end := l_end + 2; -- skip escaped quote pair
else
exit; -- real closing quote

Check warning on line 794 in source/core/ut_utils.pkb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This EXIT statement should explicitly mention the loop label.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZzvRFHIwAnQ9xtGVyei&open=AZzvRFHIwAnQ9xtGVyei&pullRequest=1312
end if;
end loop;

Expand All @@ -812,94 +812,62 @@
return l_result;
end replace_multiline_comments;

function replace_multiline_comments(a_source clob) return clob is
l_result clob;
l_ml_comment_start binary_integer := 1;
l_comment_start binary_integer := 1;
l_text_start binary_integer := 1;
l_escaped_text_start binary_integer := 1;
l_escaped_text_end_char varchar2(1 char);
l_end binary_integer := 1;
l_ml_comment clob;
l_newlines_count binary_integer;
l_offset binary_integer := 1;
l_length binary_integer := coalesce(dbms_lob.getlength(a_source), 0);
begin
l_ml_comment_start := instr(a_source,'/*');
l_comment_start := instr(a_source,'--');
l_text_start := instr(a_source,'''');
l_escaped_text_start := instr(a_source,q'[q']');
while l_offset > 0 and l_ml_comment_start > 0 loop

if l_ml_comment_start > 0 and (l_ml_comment_start < l_comment_start or l_comment_start = 0)
and (l_ml_comment_start < l_text_start or l_text_start = 0)and (l_ml_comment_start < l_escaped_text_start or l_escaped_text_start = 0)
then
l_end := instr(a_source,'*/',l_ml_comment_start+2);
append_to_clob(l_result, dbms_lob.substr(a_source, l_ml_comment_start-l_offset, l_offset));
if l_end > 0 then
l_ml_comment := substr(a_source, l_ml_comment_start, l_end-l_ml_comment_start);
l_newlines_count := length( l_ml_comment ) - length( translate( l_ml_comment, 'a'||chr(10), 'a') );
if l_newlines_count > 0 then
append_to_clob(l_result, lpad( chr(10), l_newlines_count, chr(10) ) );
end if;
l_end := l_end + 2;
end if;
else
function strip_create_header_lines(a_source dbms_preprocessor.source_lines_t)
return dbms_preprocessor.source_lines_t
is
l_result dbms_preprocessor.source_lines_t := a_source;
l_rebased dbms_preprocessor.source_lines_t;
l_create_line pls_integer;
l_header_line pls_integer;
l_header_pos pls_integer := 0;
begin
if l_result.count = 0 then
return l_result;
end if;

if l_comment_start > 0 and (l_comment_start < l_ml_comment_start or l_ml_comment_start = 0)
and (l_comment_start < l_text_start or l_text_start = 0) and (l_comment_start < l_escaped_text_start or l_escaped_text_start = 0)
then
l_end := instr(a_source,chr(10),l_comment_start+2);
if l_end > 0 then
l_end := l_end + 1;
end if;
elsif l_text_start > 0 and (l_text_start < l_ml_comment_start or l_ml_comment_start = 0)
and (l_text_start < l_comment_start or l_comment_start = 0) and (l_text_start < l_escaped_text_start or l_escaped_text_start = 0)
then
l_end := instr(a_source,q'[']',l_text_start+1);

--skip double quotes while searching for end of quoted text
while l_end > 0 and l_end = instr(a_source,q'['']',l_text_start+1) loop
l_end := instr(a_source,q'[']',l_end+1);
end loop;
if l_end > 0 then
l_end := l_end + 1;
end if;
-- remove comment lines that contain "-- create or replace" and find first CREATE
for i in 1..l_result.count loop

Check failure on line 829 in source/core/ut_utils.pkb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this FOR loop by a more robust WHILE one.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZz94vRroGBupAFrwqRQ&open=AZz94vRroGBupAFrwqRQ&pullRequest=1312
l_result(i) := regexp_replace(l_result(i), '^.*[-]{2,}\s*create(\s+or\s+replace).*$', null, 1, 1, 'i');
if l_create_line is null and regexp_like(l_result(i), '(^|[[:space:]])create([[:space:]]|$)', 'i') then
l_create_line := i;
end if;
end loop;

elsif l_escaped_text_start > 0 and (l_escaped_text_start < l_ml_comment_start or l_ml_comment_start = 0)
and (l_escaped_text_start < l_comment_start or l_comment_start = 0) and (l_escaped_text_start < l_text_start or l_text_start = 0)
then
--translate char "[" from the start of quoted text "q'[someting]'" into "]"
l_escaped_text_end_char := translate(substr(a_source, l_escaped_text_start + 2, 1),gc_open_chars,gc_close_chars);
l_end := instr(a_source,l_escaped_text_end_char||'''',l_escaped_text_start + 3 );
if l_end > 0 then
l_end := l_end + 2;
end if;
-- find first occurrence of object keyword after CREATE (may be on later line)
if l_create_line is not null then
for i in l_create_line..l_result.count loop

Check failure on line 838 in source/core/ut_utils.pkb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this FOR loop by a more robust WHILE one.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZz94vRroGBupAFrwqRR&open=AZz94vRroGBupAFrwqRR&pullRequest=1312
l_header_pos := regexp_instr(
l_result(i),
'(^|[[:space:]])(package|type|procedure|function)([[:space:]]|$)',
1, 1, 0, 'i', 2
);
if l_header_pos > 0 then
l_header_line := i;
exit;

Check warning on line 846 in source/core/ut_utils.pkb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Avoid using EXIT from within a FOR or WHILE loop.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZz94vRroGBupAFrwqRS&open=AZz94vRroGBupAFrwqRS&pullRequest=1312
end if;
end loop;

if l_end = 0 then
append_to_clob(l_result, substr(a_source, l_offset, l_length-l_offset));
else
append_to_clob(l_result, substr(a_source, l_offset, l_end-l_offset));
end if;
end if;
l_offset := l_end;
if l_offset >= l_ml_comment_start then
l_ml_comment_start := instr(a_source,'/*',l_offset);
end if;
if l_offset >= l_comment_start then
l_comment_start := instr(a_source,'--',l_offset);
end if;
if l_offset >= l_text_start then
l_text_start := instr(a_source,'''',l_offset);
end if;
if l_offset >= l_escaped_text_start then
l_escaped_text_start := instr(a_source,q'[q']',l_offset);
if l_header_line is not null then
-- keep from keyword onward on the header line
l_result(l_header_line) := substr(l_result(l_header_line), l_header_pos);
-- remove "OWNER." from create or replace statement.
-- Owner is not supported along with AUTHID - see issue https://github.com/utPLSQL/utPLSQL/issues/1088
l_result(l_header_line) := regexp_replace(
l_result(l_header_line),
'^(package|type|procedure|function)\s+("?[[:alpha:]][[:alnum:]$#_]*"?\.)(.*)',
'\1 \3', 1, 1, 'ni'
);

-- rebase so header line becomes line 1 (matches preprocessor expectations)
for i in l_header_line .. l_result.count loop

Check failure on line 862 in source/core/ut_utils.pkb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this FOR loop by a more robust WHILE one.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZz94vRroGBupAFrwqRT&open=AZz94vRroGBupAFrwqRT&pullRequest=1312
l_rebased(i - l_header_line + 1) := l_result(i);
end loop;
return l_rebased;
end if;
end loop;
append_to_clob(l_result, substr(a_source, l_end));
end if;

return l_result;
end;
end strip_create_header_lines;

function get_child_reporters(a_for_reporters ut_reporters_info := null) return ut_reporters_info is
l_for_reporters ut_reporters_info := a_for_reporters;
Expand Down
11 changes: 8 additions & 3 deletions source/core/ut_utils.pks
Original file line number Diff line number Diff line change
Expand Up @@ -406,9 +406,14 @@ create or replace package ut_utils authid definer is
* Replaces multi-line comments in given source-code with empty lines
*/
function replace_multiline_comments(a_source dbms_preprocessor.source_lines_t)
return dbms_preprocessor.source_lines_t;

function replace_multiline_comments(a_source clob) return clob;
return dbms_preprocessor.source_lines_t;

/**
* Strips the CREATE header (possibly split across lines) so source starts at
* package/type/procedure/function keyword, preserving line numbers.
*/
function strip_create_header_lines(a_source dbms_preprocessor.source_lines_t)
return dbms_preprocessor.source_lines_t;

/**
* Returns list of sub-type reporters for given list of super-type reporters
Expand Down
Loading