@@ -35,110 +35,130 @@ create or replace package body ut_coverage is
35
35
return g_develop_mode;
36
36
end;
37
37
38
- function get_cov_sources_sql(a_coverage_options ut_coverage_options) return varchar2 is
39
- l_result varchar2(32767);
40
- l_full_name varchar2(100);
41
- l_view_name varchar2(200) := ut_metadata.get_source_view_name();
38
+ function get_cov_sources_sql(a_coverage_options ut_coverage_options, a_skip_objects ut_object_names) return varchar2 is
39
+ l_result varchar2(32767);
40
+ l_full_name varchar2(32767);
41
+ l_join_mappings varchar2(32767);
42
+ l_filters varchar2(32767);
43
+ l_mappings_cardinality integer := 0;
42
44
begin
43
- if a_coverage_options.file_mappings is not null and a_coverage_options.file_mappings.count > 0 then
44
- l_full_name := 'f.file_name';
45
- else
46
- l_full_name := 'lower(s.owner||''.''||s.name)';
47
- end if;
48
- l_result := '
49
- select full_name, owner, name, line, to_be_skipped, text
50
- from (
51
- select '||l_full_name||q'[ as full_name,
52
- s.owner,
53
- s.name,
54
-
10000
s.line -
55
- coalesce(
56
- case when type!='TRIGGER' then 0 end,
57
- (select min(t.line) - 1
58
- from ]'||l_view_name||q'[ t
59
- where t.owner = s.owner and t.type = s.type and t.name = s.name
60
- and regexp_like( t.text, '[A-Za-z0-9$#_]*(begin|declare|compound).*','i'))
61
- ) as line,
62
- s.text, ]';
63
- l_result := l_result ||
64
- q'[case
65
- when
66
- -- to avoid execution of regexp_like on every line
67
- -- first do a rough check for existence of search pattern keyword
68
- (lower(s.text) like '%procedure%'
69
- or lower(s.text) like '%function%'
70
- or lower(s.text) like '%begin%'
71
- or lower(s.text) like '%end%'
72
- or lower(s.text) like '%package%'
73
- ) and
74
- regexp_like(
75
- s.text,
76
- '^([\t ]*(((not)?\s*(overriding|final|instantiable)[\t ]*)*(static|constructor|member)?[\t ]*(procedure|function)|package([\t ]+body)|begin|end([\t ]+\S+)*[ \t]*;))', 'i'
77
- )
78
- then 'Y'
79
- end as to_be_skipped ]';
80
-
81
- l_result := l_result ||' from '||l_view_name||q'[ s]';
82
-
45
+ l_result := q'[
46
+ with
47
+ trigger_source_offsets as (
48
+ select min(s.line) - 1 offset, s.owner, s.name, s.type
49
+ from {sources_view} s
50
+ where s.type = 'TRIGGER'
51
+ {filters}
52
+ and (lower(s.text) like '%begin%' or lower(s.text) like '%declare%' or lower(s.text) like '%compound%')
53
+ group by s.owner, s.name, s.type
54
+ ),
55
+ sources as (
56
+ select /*+ cardinality(f {mappings_cardinality}) */
57
+ {l_full_name} as full_name, s.owner, s.name,
58
+ s.line - case when s.type = 'TRIGGER' then o.offset else 0 end as line,
59
+ s.text
60
+ from {sources_view} s {join_file_mappings}
61
+ left join trigger_source_offsets o
62
+ on (s.owner = o.owner and s.name = o.name and s.type = o.type)
63
+ where s.type in ('PACKAGE BODY', 'TYPE BODY', 'PROCEDURE', 'FUNCTION', 'TRIGGER')
64
+ {filters}
65
+ ),
66
+ coverage_sources as (
67
+ select full_name, owner, name, line, text,
68
+ case
69
+ when
70
+ -- to avoid execution of regexp_like on every line
71
+ -- first do a rough check for existence of search pattern keyword
72
+ (lower(s.text) like '%procedure%'
73
+ or lower(s.text) like '%function%'
74
+ or lower(s.text) like '%begin%'
75
+ or lower(s.text) like '%end%'
76
+ or lower(s.text) like '%package%'
77
+ ) and
78
+ regexp_like(
79
+ s.text,
80
+ '^([\t ]*(((not)?\s*(overriding|final|instantiable)[\t ]*)*(static|constructor|member)?[\t ]*(procedure|function)|package([\t ]+body)|begin|end([\t ]+\S+)*[ \t]*;))', 'i'
81
+ )
82
+ then 'Y'
83
+ end as to_be_skipped
84
+ from sources s
85
+ )
86
+ select full_name, owner, name, line, to_be_skipped, text
87
+ from coverage_sources s
88
+ -- Exclude calls to utPLSQL framework, Unit Test packages and objects from a_exclude_list parameter of coverage reporter
89
+ where (s.owner, s.name) not in ( select /*+ cardinality(el {skipped_objects_cardinality})*/el.owner, el.name from table(:l_skipped_objects) el )
90
+ and line > 0
91
+ ]';
92
+
83
93
if a_coverage_options.file_mappings is not empty then
84
- l_result := l_result || '
94
+ l_mappings_cardinality := ut_utils.scale_cardinality(cardinality(a_coverage_options.file_mappings));
95
+ l_full_name := 'f.file_name';
96
+ l_join_mappings := '
85
97
join table(:file_mappings) f
86
98
on s.name = f.object_name
87
99
and s.type = f.object_type
88
- and s.owner = f.object_owner
89
- where 1 = 1';
90
- elsif a_coverage_options.include_objects is not empty then
91
- l_result := l_result || '
92
- where (s.owner, s.name) in (select il.owner, il.name from table(:include_objects) il)';
100
+ and s.owner = f.object_owner';
93
101
else
94
- l_result := l_result || '
95
- where s.owner in (select upper(t.column_value) from table(:l_schema_names) t)';
102
+ l_full_name := q'[lower(s.owner||'.'||s.name)]';
103
+ l_filters := case
104
+ when a_coverage_options.include_objects is not empty then '
105
+ and (s.owner, s.name) in (
106
+ select /*+ cardinality(il '||ut_utils.scale_cardinality(cardinality(a_coverage_options.include_objects))||') */
107
+ il.owner, il.name
108
+ from table(:include_objects) il
109
+ )'
110
+ else '
111
+ and s.owner in (
112
+ select /*+ cardinality(t '||ut_utils.scale_cardinality(cardinality(a_coverage_options.schema_names))||') */
113
+ upper(t.column_value)
114
+ from table(:l_schema_names) t)'
115
+ end;
96
116
end if;
97
- l_result := l_result || q'[
98
- and s.type not in ('PACKAGE', 'TYPE', 'JAVA SOURCE')
99
- --Exclude calls to utPLSQL framework, Unit Test packages and objects from a_exclude_list parameter of coverage reporter
100
- and (s.owner, s.name) not in (select el.owner, el.name from table(:l_skipped_objects) el)
101
- )
102
- where line > 0]';
117
+
118
+ l_result := replace(l_result, '{sources_view}', ut_metadata.get_source_view_name());
119
+ l_result := replace(l_result, '{l_full_name}', l_full_name);
120
+ l_result := replace(l_result, '{join_file_mappings}', l_join_mappings);
121
+ l_result := replace(l_result, '{filters}', l_filters);
122
+ l_result := replace(l_result, '{mappings_cardinality}', l_mappings_cardinality);
123
+ l_result := replace(l_result, '{skipped_objects_cardinality}', ut_utils.scale_cardinality(cardinality(a_skip_objects)));
124
+
103
125
return l_result;
126
+
104
127
end;
105
128
106
- function get_cov_sources_cursor(a_coverage_options in ut_coverage_options,a_sql in varchar2 ) return sys_refcursor is
129
+ function get_cov_sources_cursor(a_coverage_options in ut_coverage_options) return sys_refcursor is
107
130
l_cursor sys_refcursor;
108
131
l_skip_objects ut_object_names;
109
132
l_sql varchar2(32767);
110
- l_valid_pattern varchar2(250) := '^\s*select.+$';
111
133
begin
112
134
if not is_develop_mode() then
113
135
--skip all the utplsql framework objects and all the unit test packages that could potentially be reported by coverage.
114
136
l_skip_objects := ut_utils.get_utplsql_objects_list() multiset union all coalesce(a_coverage_options.exclude_objects, ut_object_names());
115
137
end if;
116
- if regexp_like(a_sql, l_valid_pattern, 'mi') then
117
- -- pseudo assert for PL/SQL Cop
118
- l_sql := sys.dbms_assert.noop(a_sql);
119
- else
120
- raise_application_error(-20542, 'Possible SQL injection detected. a_sql parameter does not match valid pattern "' || l_valid_pattern || '".');
121
- end if;
138
+
139
+ l_sql := get_cov_sources_sql(a_coverage_options, l_skip_objects);
140
+
141
+ ut_event_manager.trigger_event(ut_event_manager.gc_debug, ut_key_anyvalues().put('l_sql',l_sql) );
142
+
122
143
if a_coverage_options.file_mappings is not empty then
123
144
open l_cursor for l_sql using a_coverage_options.file_mappings, l_skip_objects;
124
145
elsif a_coverage_options.include_objects is not empty then
125
- open l_cursor for l_sql using a_coverage_options.include_objects, l_skip_objects;
146
+ open l_cursor for l_sql using a_coverage_options.include_objects, a_coverage_options.include_objects, l_skip_objects;
126
147
else
127
- open l_cursor for l_sql using a_coverage_options.schema_names, l_skip_objects;
148
+ open l_cursor for l_sql using a_coverage_options.schema_names, a_coverage_options.schema_names, l_skip_objects;
128
149
end if;
129
150
return l_cursor;
130
151
end;
131
152
132
- procedure populate_tmp_table(a_coverage_options ut_coverage_options, a_sql in varchar2 ) is
153
+ procedure populate_tmp_table(a_coverage_options ut_coverage_options) is
133
154
pragma autonomous_transaction;
134
155
l_cov_sources_crsr sys_refcursor;
135
156
l_cov_sources_data ut_coverage_helper.t_coverage_sources_tmp_rows;
136
157
begin
137
158
138
159
if not ut_coverage_helper.is_tmp_table_populated() or is_develop_mode() then
139
160
ut_coverage_helper.cleanup_tmp_table();
140
- ut_event_manager.trigger_event(ut_event_manager.gc_debug, ut_key_anyvalues().put('a_sql',a_sql) );
141
- l_cov_sources_crsr := get_cov_sources_cursor(a_coverage_options, a_sql);
161
+ l_cov_sources_crsr := get_cov_sources_cursor(a_coverage_options);
142
162
143
163
loop
144
164
fetch l_cov_sources_crsr bulk collect into l_cov_sources_data limit 10000;
@@ -212,7 +232,7 @@ create or replace package body ut_coverage is
212
232
begin
213
233
--prepare global temp table with sources
214
234
ut_event_manager.trigger_event('about to populate coverage temp table');
215
- populate_tmp_table(a_coverage_options, get_cov_sources_sql(a_coverage_options) );
235
+ populate_tmp_table(a_coverage_options);
216
236
ut_event_manager.trigger_event('coverage temp table populated');
217
237
218
238
-- Get raw data for both reporters, order is important as tmp table will skip headers and dont populate
0 commit comments