From abba642181ff9a94f48a913947e18acc3af934c5 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Sat, 11 May 2019 02:02:59 +0100 Subject: [PATCH 1/4] Trying different approach to coverage gathering. --- source/core/coverage/ut_coverage.pkb | 167 ++++++++++++++++++++------- 1 file changed, 125 insertions(+), 42 deletions(-) diff --git a/source/core/coverage/ut_coverage.pkb b/source/core/coverage/ut_coverage.pkb index 5754cf4e9..18df1487f 100644 --- a/source/core/coverage/ut_coverage.pkb +++ b/source/core/coverage/ut_coverage.pkb @@ -36,32 +36,39 @@ create or replace package body ut_coverage is end; function get_cov_sources_sql(a_coverage_options ut_coverage_options) return varchar2 is - l_result varchar2(32767); - l_full_name varchar2(100); - l_view_name varchar2(200) := ut_metadata.get_source_view_name(); + l_result varchar2(32767); + l_full_name varchar2(32767); + l_mappings varchar2(32767); + l_filters varchar2(32767); + l_mappings_cardinality integer := 0; begin - if a_coverage_options.file_mappings is not null and a_coverage_options.file_mappings.count > 0 then - l_full_name := 'f.file_name'; - else - l_full_name := 'lower(s.owner||''.''||s.name)'; - end if; - l_result := ' - select full_name, owner, name, line, to_be_skipped, text - from ( - select '||l_full_name||q'[ as full_name, + l_result := q'[ + with sources as ( + select /*+ cardinality(f {mappings_cardinality}) */ + {l_full_name} as full_name, s.owner, s.name, s.line, s.text + from {sources_view} s {file_mappings} + where s.type in ('PACKAGE BODY', 'TYPE BODY', 'PROCEDURE', 'FUNCTION') + {filters} + ), + trigger_sources as ( + select /*+ cardinality(f {mappings_cardinality}) */ + {l_full_name} as full_name, s.owner, s.name, - s.line - - coalesce( - case when type!='TRIGGER' then 0 end, - (select min(t.line) - 1 - from ]'||l_view_name||q'[ t + s.line + - (select min(t.line) - 1 + from {sources_view} t where t.owner = s.owner and t.type = s.type and t.name = s.name - and regexp_like( t.text, '[A-Za-z0-9$#_]*(begin|declare|compound).*','i')) - ) as line, - s.text, ]'; - l_result := l_result || - q'[case + and regexp_like( t.text, '[A-Za-z0-9$#_]*(begin|declare|compound).*', 'i' ) + ) as line, + s.text + from {sources_view} s {file_mappings} + where s.type = 'TRIGGER' + {filters} + ), + coverage_sources as ( + select full_name, owner, name, line, text, + case when -- to avoid execution of regexp_like on every line -- first do a rough check for existence of search pattern keyword @@ -76,31 +83,107 @@ create or replace package body ut_coverage is '^([\t ]*(((not)?\s*(overriding|final|instantiable)[\t ]*)*(static|constructor|member)?[\t ]*(procedure|function)|package([\t ]+body)|begin|end([\t ]+\S+)*[ \t]*;))', 'i' ) then 'Y' - end as to_be_skipped ]'; + end as to_be_skipped + from ( + select * from sources + union all + select * from trigger_sources + ) s + ) + select full_name, owner, name, line, to_be_skipped, text + from coverage_sources s + --Exclude calls to utPLSQL framework, Unit Test packages and objects from a_exclude_list parameter of coverage reporter + where (s.owner, s.name) not in ( select el.owner, el.name from table(:l_skipped_objects) el ) + and line > 0 + ]'; - l_result := l_result ||' from '||l_view_name||q'[ s]'; - if a_coverage_options.file_mappings is not empty then - l_result := l_result || ' + l_mappings_cardinality := ut_utils.scale_cardinality(cardinality(a_coverage_options.file_mappings)); + l_full_name := 'f.file_name'; + l_mappings := ' join table(:file_mappings) f on s.name = f.object_name and s.type = f.object_type - and s.owner = f.object_owner - where 1 = 1'; - elsif a_coverage_options.include_objects is not empty then - l_result := l_result || ' - where (s.owner, s.name) in (select il.owner, il.name from table(:include_objects) il)'; + and s.owner = f.object_owner'; else - l_result := l_result || ' - where s.owner in (select upper(t.column_value) from table(:l_schema_names) t)'; + l_full_name := q'[lower(s.owner||'.'||s.name)]'; + l_filters := case + when a_coverage_options.include_objects is not empty then ' + and (s.owner, s.name) in (select il.owner, il.name from table(:include_objects) il)' + else ' + and s.owner in (select upper(t.column_value) from table(:l_schema_names) t)' + end; end if; - l_result := l_result || q'[ - and s.type not in ('PACKAGE', 'TYPE', 'JAVA SOURCE') - --Exclude calls to utPLSQL framework, Unit Test packages and objects from a_exclude_list parameter of coverage reporter - and (s.owner, s.name) not in (select el.owner, el.name from table(:l_skipped_objects) el) - ) - where line > 0]'; + + l_result := replace(l_result, '{sources_view}', ut_metadata.get_source_view_name()); + l_result := replace(l_result, '{l_full_name}', l_full_name); + l_result := replace(l_result, '{file_mappings}', l_mappings); + l_result := replace(l_result, '{filters}', l_filters); + l_result := replace(l_result, '{mappings_cardinality}', l_mappings_cardinality); + return l_result; + +-- if a_coverage_options.file_mappings is not null and a_coverage_options.file_mappings.count > 0 then +-- l_full_name := 'f.file_name'; +-- else +-- l_full_name := 'lower(s.owner||''.''||s.name)'; +-- end if; +-- l_result := ' +-- select full_name, owner, name, line, to_be_skipped, text +-- from ( +-- select '||l_full_name||q'[ as full_name, +-- s.owner, +-- s.name, +-- s.line - +-- coalesce( +-- case when type!='TRIGGER' then 0 end, +-- (select min(t.line) - 1 +-- from ]'||ut_metadata.get_source_view_name()||q'[ t +-- where t.owner = s.owner and t.type = s.type and t.name = s.name +-- and regexp_like( t.text, '[A-Za-z0-9$#_]*(begin|declare|compound).*','i')) +-- ) as line, +-- s.text, ]'; +-- l_result := l_result || +-- q'[case +-- when +-- -- to avoid execution of regexp_like on every line +-- -- first do a rough check for existence of search pattern keyword +-- (lower(s.text) like '%procedure%' +-- or lower(s.text) like '%function%' +-- or lower(s.text) like '%begin%' +-- or lower(s.text) like '%end%' +-- or lower(s.text) like '%package%' +-- ) and +-- regexp_like( +-- s.text, +-- '^([\t ]*(((not)?\s*(overriding|final|instantiable)[\t ]*)*(static|constructor|member)?[\t ]*(procedure|function)|package([\t ]+body)|begin|end([\t ]+\S+)*[ \t]*;))', 'i' +-- ) +-- then 'Y' +-- end as to_be_skipped ]'; +-- +-- l_result := l_result ||' from '||ut_metadata.get_source_view_name()||q'[ s]'; +-- +-- if a_coverage_options.file_mappings is not empty then +-- l_result := l_result || ' +-- join table(:file_mappings) f +-- on s.name = f.object_name +-- and s.type = f.object_type +-- and s.owner = f.object_owner +-- where 1 = 1'; +-- elsif a_coverage_options.include_objects is not empty then +-- l_result := l_result || ' +-- where (s.owner, s.name) in (select il.owner, il.name from table(:include_objects) il)'; +-- else +-- l_result := l_result || ' +-- where s.owner in (select upper(t.column_value) from table(:l_schema_names) t)'; +-- end if; +-- l_result := l_result || q'[ +-- and s.type not in ('PACKAGE', 'TYPE', 'JAVA SOURCE') +-- --Exclude calls to utPLSQL framework, Unit Test packages and objects from a_exclude_list parameter of coverage reporter +-- and (s.owner, s.name) not in (select el.owner, el.name from table(:l_skipped_objects) el) +-- ) +-- where line > 0]'; +-- return l_result; end; function get_cov_sources_cursor(a_coverage_options in ut_coverage_options,a_sql in varchar2) return sys_refcursor is @@ -120,11 +203,11 @@ create or replace package body ut_coverage is raise_application_error(-20542, 'Possible SQL injection detected. a_sql parameter does not match valid pattern "' || l_valid_pattern || '".'); end if; if a_coverage_options.file_mappings is not empty then - open l_cursor for l_sql using a_coverage_options.file_mappings, l_skip_objects; + open l_cursor for l_sql using a_coverage_options.file_mappings, a_coverage_options.file_mappings, l_skip_objects; elsif a_coverage_options.include_objects is not empty then - open l_cursor for l_sql using a_coverage_options.include_objects, l_skip_objects; + open l_cursor for l_sql using a_coverage_options.include_objects, a_coverage_options.include_objects, l_skip_objects; else - open l_cursor for l_sql using a_coverage_options.schema_names, l_skip_objects; + open l_cursor for l_sql using a_coverage_options.schema_names, a_coverage_options.schema_names, l_skip_objects; end if; return l_cursor; end; From a8c0c82d8454cda6fa3bcf08b8aebb02040104b0 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Mon, 13 May 2019 08:18:37 +0100 Subject: [PATCH 2/4] Trying different approach to coverage gathering. --- source/core/coverage/ut_coverage.pkb | 71 ++-------------------------- 1 file changed, 5 insertions(+), 66 deletions(-) diff --git a/source/core/coverage/ut_coverage.pkb b/source/core/coverage/ut_coverage.pkb index 18df1487f..f87ba8249 100644 --- a/source/core/coverage/ut_coverage.pkb +++ b/source/core/coverage/ut_coverage.pkb @@ -38,7 +38,7 @@ create or replace package body ut_coverage is function get_cov_sources_sql(a_coverage_options ut_coverage_options) return varchar2 is l_result varchar2(32767); l_full_name varchar2(32767); - l_mappings varchar2(32767); + l_join_mappings varchar2(32767); l_filters varchar2(32767); l_mappings_cardinality integer := 0; begin @@ -46,7 +46,7 @@ create or replace package body ut_coverage is with sources as ( select /*+ cardinality(f {mappings_cardinality}) */ {l_full_name} as full_name, s.owner, s.name, s.line, s.text - from {sources_view} s {file_mappings} + from {sources_view} s {join_file_mappings} where s.type in ('PACKAGE BODY', 'TYPE BODY', 'PROCEDURE', 'FUNCTION') {filters} ), @@ -62,7 +62,7 @@ create or replace package body ut_coverage is and regexp_like( t.text, '[A-Za-z0-9$#_]*(begin|declare|compound).*', 'i' ) ) as line, s.text - from {sources_view} s {file_mappings} + from {sources_view} s {join_file_mappings} where s.type = 'TRIGGER' {filters} ), @@ -100,7 +100,7 @@ create or replace package body ut_coverage is if a_coverage_options.file_mappings is not empty then l_mappings_cardinality := ut_utils.scale_cardinality(cardinality(a_coverage_options.file_mappings)); l_full_name := 'f.file_name'; - l_mappings := ' + l_join_mappings := ' join table(:file_mappings) f on s.name = f.object_name and s.type = f.object_type @@ -117,73 +117,12 @@ create or replace package body ut_coverage is l_result := replace(l_result, '{sources_view}', ut_metadata.get_source_view_name()); l_result := replace(l_result, '{l_full_name}', l_full_name); - l_result := replace(l_result, '{file_mappings}', l_mappings); + l_result := replace(l_result, '{join_file_mappings}', l_join_mappings); l_result := replace(l_result, '{filters}', l_filters); l_result := replace(l_result, '{mappings_cardinality}', l_mappings_cardinality); return l_result; --- if a_coverage_options.file_mappings is not null and a_coverage_options.file_mappings.count > 0 then --- l_full_name := 'f.file_name'; --- else --- l_full_name := 'lower(s.owner||''.''||s.name)'; --- end if; --- l_result := ' --- select full_name, owner, name, line, to_be_skipped, text --- from ( --- select '||l_full_name||q'[ as full_name, --- s.owner, --- s.name, --- s.line - --- coalesce( --- case when type!='TRIGGER' then 0 end, --- (select min(t.line) - 1 --- from ]'||ut_metadata.get_source_view_name()||q'[ t --- where t.owner = s.owner and t.type = s.type and t.name = s.name --- and regexp_like( t.text, '[A-Za-z0-9$#_]*(begin|declare|compound).*','i')) --- ) as line, --- s.text, ]'; --- l_result := l_result || --- q'[case --- when --- -- to avoid execution of regexp_like on every line --- -- first do a rough check for existence of search pattern keyword --- (lower(s.text) like '%procedure%' --- or lower(s.text) like '%function%' --- or lower(s.text) like '%begin%' --- or lower(s.text) like '%end%' --- or lower(s.text) like '%package%' --- ) and --- regexp_like( --- s.text, --- '^([\t ]*(((not)?\s*(overriding|final|instantiable)[\t ]*)*(static|constructor|member)?[\t ]*(procedure|function)|package([\t ]+body)|begin|end([\t ]+\S+)*[ \t]*;))', 'i' --- ) --- then 'Y' --- end as to_be_skipped ]'; --- --- l_result := l_result ||' from '||ut_metadata.get_source_view_name()||q'[ s]'; --- --- if a_coverage_options.file_mappings is not empty then --- l_result := l_result || ' --- join table(:file_mappings) f --- on s.name = f.object_name --- and s.type = f.object_type --- and s.owner = f.object_owner --- where 1 = 1'; --- elsif a_coverage_options.include_objects is not empty then --- l_result := l_result || ' --- where (s.owner, s.name) in (select il.owner, il.name from table(:include_objects) il)'; --- else --- l_result := l_result || ' --- where s.owner in (select upper(t.column_value) from table(:l_schema_names) t)'; --- end if; --- l_result := l_result || q'[ --- and s.type not in ('PACKAGE', 'TYPE', 'JAVA SOURCE') --- --Exclude calls to utPLSQL framework, Unit Test packages and objects from a_exclude_list parameter of coverage reporter --- and (s.owner, s.name) not in (select el.owner, el.name from table(:l_skipped_objects) el) --- ) --- where line > 0]'; --- return l_result; end; function get_cov_sources_cursor(a_coverage_options in ut_coverage_options,a_sql in varchar2) return sys_refcursor is From f4bede09214a78636ee0a836a25b17a843366697 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Wed, 17 Jul 2019 02:31:12 +0100 Subject: [PATCH 3/4] Reworked coverage sources query --- source/core/coverage/ut_coverage.pkb | 134 +++++++++++++-------------- source/core/coverage/ut_coverage.pks | 3 +- 2 files changed, 67 insertions(+), 70 deletions(-) diff --git a/source/core/coverage/ut_coverage.pkb b/source/core/coverage/ut_coverage.pkb index f87ba8249..caa75ae51 100644 --- a/source/core/coverage/ut_coverage.pkb +++ b/source/core/coverage/ut_coverage.pkb @@ -35,7 +35,7 @@ create or replace package body ut_coverage is return g_develop_mode; end; - function get_cov_sources_sql(a_coverage_options ut_coverage_options) return varchar2 is + function get_cov_sources_sql(a_coverage_options ut_coverage_options, a_skip_objects ut_object_names) return varchar2 is l_result varchar2(32767); l_full_name varchar2(32767); l_join_mappings varchar2(32767); @@ -43,58 +43,51 @@ create or replace package body ut_coverage is l_mappings_cardinality integer := 0; begin l_result := q'[ - with sources as ( - select /*+ cardinality(f {mappings_cardinality}) */ - {l_full_name} as full_name, s.owner, s.name, s.line, s.text - from {sources_view} s {join_file_mappings} - where s.type in ('PACKAGE BODY', 'TYPE BODY', 'PROCEDURE', 'FUNCTION') - {filters} - ), - trigger_sources as ( - select /*+ cardinality(f {mappings_cardinality}) */ - {l_full_name} as full_name, - s.owner, - s.name, - s.line - - (select min(t.line) - 1 - from {sources_view} t - where t.owner = s.owner and t.type = s.type and t.name = s.name - and regexp_like( t.text, '[A-Za-z0-9$#_]*(begin|declare|compound).*', 'i' ) - ) as line, - s.text - from {sources_view} s {join_file_mappings} - where s.type = 'TRIGGER' - {filters} - ), - coverage_sources as ( - select full_name, owner, name, line, text, - case - when - -- to avoid execution of regexp_like on every line - -- first do a rough check for existence of search pattern keyword - (lower(s.text) like '%procedure%' - or lower(s.text) like '%function%' - or lower(s.text) like '%begin%' - or lower(s.text) like '%end%' - or lower(s.text) like '%package%' - ) and - regexp_like( - s.text, - '^([\t ]*(((not)?\s*(overriding|final|instantiable)[\t ]*)*(static|constructor|member)?[\t ]*(procedure|function)|package([\t ]+body)|begin|end([\t ]+\S+)*[ \t]*;))', 'i' - ) - then 'Y' - end as to_be_skipped - from ( - select * from sources - union all - select * from trigger_sources - ) s - ) - select full_name, owner, name, line, to_be_skipped, text - from coverage_sources s - --Exclude calls to utPLSQL framework, Unit Test packages and objects from a_exclude_list parameter of coverage reporter - where (s.owner, s.name) not in ( select el.owner, el.name from table(:l_skipped_objects) el ) - and line > 0 + with + trigger_source_offsets as ( + select min(s.line) - 1 offset, s.owner, s.name, s.type + from {sources_view} s + where s.type = 'TRIGGER' + {filters} + and (lower(s.text) like '%begin%' or lower(s.text) like '%declare%' or lower(s.text) like '%compound%') + group by s.owner, s.name, s.type + ), + sources as ( + select /*+ cardinality(f {mappings_cardinality}) */ + {l_full_name} as full_name, s.owner, s.name, + s.line - case when s.type = 'TRIGGER' then o.offset else 0 end as line, + s.text + from {sources_view} s {join_file_mappings} + left join trigger_source_offsets o + on (s.owner = o.owner and s.name = o.name and s.type = o.type) + where s.type in ('PACKAGE BODY', 'TYPE BODY', 'PROCEDURE', 'FUNCTION', 'TRIGGER') + {filters} + ), + coverage_sources as ( + select full_name, owner, name, line, text, + case + when + -- to avoid execution of regexp_like on every line + -- first do a rough check for existence of search pattern keyword + (lower(s.text) like '%procedure%' + or lower(s.text) like '%function%' + or lower(s.text) like '%begin%' + or lower(s.text) like '%end%' + or lower(s.text) like '%package%' + ) and + regexp_like( + s.text, + '^([\t ]*(((not)?\s*(overriding|final|instantiable)[\t ]*)*(static|constructor|member)?[\t ]*(procedure|function)|package([\t ]+body)|begin|end([\t ]+\S+)*[ \t]*;))', 'i' + ) + then 'Y' + end as to_be_skipped + from sources s + ) + select full_name, owner, name, line, to_be_skipped, text + from coverage_sources s + -- Exclude calls to utPLSQL framework, Unit Test packages and objects from a_exclude_list parameter of coverage reporter + where (s.owner, s.name) not in ( select /*+ cardinality(el {skipped_objects_cardinality})*/el.owner, el.name from table(:l_skipped_objects) el ) + and line > 0 ]'; if a_coverage_options.file_mappings is not empty then @@ -109,9 +102,16 @@ create or replace package body ut_coverage is l_full_name := q'[lower(s.owner||'.'||s.name)]'; l_filters := case when a_coverage_options.include_objects is not empty then ' - and (s.owner, s.name) in (select il.owner, il.name from table(:include_objects) il)' + and (s.owner, s.name) in ( + select /*+ cardinality(il '||ut_utils.scale_cardinality(cardinality(a_coverage_options.include_objects))||') */ + il.owner, il.name + from table(:include_objects) il + )' else ' - and s.owner in (select upper(t.column_value) from table(:l_schema_names) t)' + and s.owner in ( + select /*+ cardinality(t '||ut_utils.scale_cardinality(cardinality(a_coverage_options.schema_names))||') */ + upper(t.column_value) + from table(:l_schema_names) t)' end; end if; @@ -120,29 +120,28 @@ create or replace package body ut_coverage is l_result := replace(l_result, '{join_file_mappings}', l_join_mappings); l_result := replace(l_result, '{filters}', l_filters); l_result := replace(l_result, '{mappings_cardinality}', l_mappings_cardinality); + l_result := replace(l_result, '{skipped_objects_cardinality}', ut_utils.scale_cardinality(cardinality(a_skip_objects))); return l_result; end; - function get_cov_sources_cursor(a_coverage_options in ut_coverage_options,a_sql in varchar2) return sys_refcursor is + function get_cov_sources_cursor(a_coverage_options in ut_coverage_options) return sys_refcursor is l_cursor sys_refcursor; l_skip_objects ut_object_names; l_sql varchar2(32767); - l_valid_pattern varchar2(250) := '^\s*select.+$'; begin if not is_develop_mode() then --skip all the utplsql framework objects and all the unit test packages that could potentially be reported by coverage. l_skip_objects := ut_utils.get_utplsql_objects_list() multiset union all coalesce(a_coverage_options.exclude_objects, ut_object_names()); end if; - if regexp_like(a_sql, l_valid_pattern, 'mi') then - -- pseudo assert for PL/SQL Cop - l_sql := sys.dbms_assert.noop(a_sql); - else - raise_application_error(-20542, 'Possible SQL injection detected. a_sql parameter does not match valid pattern "' || l_valid_pattern || '".'); - end if; + + l_sql := get_cov_sources_sql(a_coverage_options, l_skip_objects); + + ut_event_manager.trigger_event(ut_event_manager.gc_debug, ut_key_anyvalues().put('l_sql',l_sql) ); + if a_coverage_options.file_mappings is not empty then - open l_cursor for l_sql using a_coverage_options.file_mappings, a_coverage_options.file_mappings, l_skip_objects; + open l_cursor for l_sql using a_coverage_options.file_mappings, l_skip_objects; elsif a_coverage_options.include_objects is not empty then open l_cursor for l_sql using a_coverage_options.include_objects, a_coverage_options.include_objects, l_skip_objects; else @@ -151,7 +150,7 @@ create or replace package body ut_coverage is return l_cursor; end; - procedure populate_tmp_table(a_coverage_options ut_coverage_options, a_sql in varchar2) is + procedure populate_tmp_table(a_coverage_options ut_coverage_options) is pragma autonomous_transaction; l_cov_sources_crsr sys_refcursor; l_cov_sources_data ut_coverage_helper.t_coverage_sources_tmp_rows; @@ -159,8 +158,7 @@ create or replace package body ut_coverage is if not ut_coverage_helper.is_tmp_table_populated() or is_develop_mode() then ut_coverage_helper.cleanup_tmp_table(); - ut_event_manager.trigger_event(ut_event_manager.gc_debug, ut_key_anyvalues().put('a_sql',a_sql) ); - l_cov_sources_crsr := get_cov_sources_cursor(a_coverage_options, a_sql); + l_cov_sources_crsr := get_cov_sources_cursor(a_coverage_options); loop fetch l_cov_sources_crsr bulk collect into l_cov_sources_data limit 10000; @@ -234,7 +232,7 @@ create or replace package body ut_coverage is begin --prepare global temp table with sources ut_event_manager.trigger_event('about to populate coverage temp table'); - populate_tmp_table(a_coverage_options, get_cov_sources_sql(a_coverage_options)); + populate_tmp_table(a_coverage_options); ut_event_manager.trigger_event('coverage temp table populated'); -- Get raw data for both reporters, order is important as tmp table will skip headers and dont populate diff --git a/source/core/coverage/ut_coverage.pks b/source/core/coverage/ut_coverage.pks index 9254e6d37..1d87d50c9 100644 --- a/source/core/coverage/ut_coverage.pks +++ b/source/core/coverage/ut_coverage.pks @@ -18,7 +18,6 @@ create or replace package ut_coverage authid current_user is gc_proftab_coverage constant varchar2(32) := 'proftab'; gc_block_coverage constant varchar2(32) := 'block'; - gc_extended_coverage constant varchar2(32) := 'extended'; type tt_coverage_id_arr is table of integer index by varchar2(30); @@ -75,7 +74,7 @@ create or replace package ut_coverage authid current_user is * Allows overwriting of private global variable g_coverage_id * Used internally, only for unit testing of the framework only */ - procedure mock_coverage_id(a_coverage_id integer,a_coverage_type in varchar2); + procedure mock_coverage_id(a_coverage_id integer, a_coverage_type in varchar2); procedure mock_coverage_id(a_coverage_id tt_coverage_id_arr); From a95ca5555c964d1aaa0bd7e8234e7e77810c4b07 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Wed, 17 Jul 2019 22:58:19 +0100 Subject: [PATCH 4/4] simplified profiler helper - no need for dynamic SQL and explicit cursor. --- .../coverage/ut_coverage_helper_profiler.pkb | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/source/core/coverage/ut_coverage_helper_profiler.pkb b/source/core/coverage/ut_coverage_helper_profiler.pkb index 6b685ede0..c90a5b36a 100644 --- a/source/core/coverage/ut_coverage_helper_profiler.pkb +++ b/source/core/coverage/ut_coverage_helper_profiler.pkb @@ -54,26 +54,23 @@ create or replace package body ut_coverage_helper_profiler is end; function proftab_results(a_object_owner varchar2, a_object_name varchar2, a_coverage_id integer) return t_proftab_rows is - l_raw_coverage sys_refcursor; - l_coverage_rows t_proftab_rows; + l_coverage_rows t_proftab_rows; begin - open l_raw_coverage for q'[select d.line#, + select + d.line#, case when sum(d.total_occur) = 0 and sum(d.total_time) > 0 then 1 else sum(d.total_occur) end total_occur - from plsql_profiler_units u - join plsql_profiler_data d - on u.runid = d.runid - and u.unit_number = d.unit_number - where u.runid = :a_coverage_id - and u.unit_owner = :a_object_owner - and u.unit_name = :a_object_name - and u.unit_type not in ('PACKAGE SPEC', 'TYPE SPEC', 'ANONYMOUS BLOCK') - group by d.line#]' using a_coverage_id,a_object_owner,a_object_name; + bulk collect into l_coverage_rows + from plsql_profiler_units u + join plsql_profiler_data d + on u.runid = d.runid + and u.unit_number = d.unit_number + where u.runid = a_coverage_id + and u.unit_owner = a_object_owner + and u.unit_name = a_object_name + and u.unit_type in ('PACKAGE BODY', 'TYPE BODY', 'PROCEDURE', 'FUNCTION', 'TRIGGER') + group by d.line#; - FETCH l_raw_coverage BULK COLLECT - INTO l_coverage_rows; - CLOSE l_raw_coverage; - - RETURN l_coverage_rows; + return l_coverage_rows; end; function get_raw_coverage_data(a_object_owner varchar2, a_object_name varchar2, a_coverage_id integer) return ut_coverage_helper.t_unit_line_calls is