Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit df2c763

Browse files
committed
Added cache mechanism to ut_annotation_parser.
Changed the way schema is scanned, so that all annotations/sources are scanned with single execution of a query. TODO - change the way annotations are parsed, so that we can scan for "floating" annotations. Annotations that are on top of suite, but associated with procedure, are not valid suite-level annotations. This is why the package `old_tests/helpers/test_reporters` was updated.
1 parent ac23f15 commit df2c763

20 files changed

Lines changed: 998 additions & 716 deletions

old_tests/helpers/test_reporters.pks

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ create or replace package test_reporters
22
as
33
--%suite(A suite for testing different outcomes from reporters)
44
--%suitepath(org.utplsql.utplsql.test)
5+
56
--%beforeall
67
procedure beforeall;
78

old_tests/ut_suite_manager/ut_suite_manager.CacheInvalidaesOnPackageDrop.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ begin
3434
select * bulk collect into l_test_report from table(ut.run(user || '.tst_package_to_be_dropped'));
3535
exception
3636
when others then
37-
if sqlerrm like '%tst_package_to_be_dropped%does not exist%' then
37+
if sqlerrm like '%tst_package_to_be_dropped%not found%' then
3838
:test_result := ut_utils.tr_success;
3939
end if;
4040
end;
4141
if :test_result != ut_utils.tr_success or :test_result is null then
42-
dbms_output.put_line('Failed: Expected exception with text like ''%tst_package_to_be_dropped%does not exist%'' but got:''' ||
42+
dbms_output.put_line('Failed: Expected exception with text like ''%tst_package_to_be_dropped%not found%'' but got:''' ||
4343
sqlerrm || '''');
4444
end if;
4545
end;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
create type ut_annotated_object as object(
2+
object_owner varchar2(250),
3+
object_name varchar2(250),
4+
object_type varchar2(50),
5+
annotations ut_annotations
6+
)
7+
/
8+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
create type ut_annotated_objects as table of ut_annotated_object
2+
/
3+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
create table ut_annotation_cache (
2+
cache_id number(20,0) not null,
3+
annotation_position number(5,0) not null,
4+
annotation_name varchar2(1000) not null,
5+
annotation_text varchar2(4000),
6+
subobject_name varchar2(250),
7+
-- subobject_name_not_null varchar2(250) generated always as (nvl(subobject_name,'null')) not null,
8+
constraint ut_annotation_cache_pk primary key(cache_id, annotation_position),
9+
-- constraint ut_annotation_cache_pk primary key(cache_id, annotation_position, subobject_name_not_null),
10+
constraint ut_annotation_cache_fk foreign key(cache_id) references ut_annotation_cache_info(cache_id) on delete cascade
11+
);
12+
13+
create index ut_annotation_cache_fk on ut_annotation_cache(cache_id);
14+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
create table ut_annotation_cache_info (
2+
cache_id number(20,0) not null,
3+
object_owner varchar2(250) not null,
4+
object_name varchar2(250) not null,
5+
object_type varchar2(250) not null,
6+
parse_time date not null,
7+
is_annotated varchar2(1) not null,
8+
constraint ut_annotation_cache_info_pk primary key(cache_id),
9+
constraint ut_annotation_cache_info_uk unique (object_owner, object_name, object_type),
10+
constraint ut_annotation_cache_info_ck1 check (is_annotated in ('Y','N'))
11+
) organization index;
12+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
create or replace package body ut_annotation_cache_manager as
2+
3+
procedure update_cache(a_object ut_annotated_object, a_cache_id integer) is
4+
l_is_annotated varchar2(1);
5+
v_cache_id integer := a_cache_id;
6+
l_current_schema varchar2(250) := ut_utils.ut_owner;
7+
pragma autonomous_transaction;
8+
begin
9+
if a_object.annotations is not null and a_object.annotations.count > 0 then
10+
l_is_annotated := 'Y';
11+
else
12+
l_is_annotated := 'N';
13+
end if;
14+
15+
if v_cache_id is not null then
16+
update ut_annotation_cache_info i
17+
set i.parse_time = sysdate,
18+
i.is_annotated = l_is_annotated
19+
where i.cache_id = v_cache_id;
20+
else
21+
insert into ut_annotation_cache_info
22+
(cache_id, object_owner, object_name, object_type, parse_time, is_annotated)
23+
values (ut_annotation_cache_seq.nextval, a_object.object_owner, a_object.object_name, a_object.object_type, sysdate, l_is_annotated)
24+
returning cache_id into v_cache_id;
25+
end if;
26+
27+
delete from ut_annotation_cache c
28+
where cache_id = v_cache_id;
29+
30+
if l_is_annotated = 'Y' then
31+
-- begin
32+
insert into ut_annotation_cache
33+
(cache_id, annotation_position, annotation_name, annotation_text, subobject_name)
34+
select v_cache_id, a.position, a.name, a.text, a.subobject_name
35+
from table(a_object.annotations) a;
36+
--TODO - duplicate annotations found?? - should not happen, getting standalone annotations need to happen after procedure annotations were parsed
37+
-- exception
38+
-- when others then
39+
-- dbms_output.put_line(xmltype(anydata.convertCollection(a_object.annotations)).getclobval);
40+
-- raise;
41+
-- end;
42+
end if;
43+
commit;
44+
end;
45+
46+
end;
47+
/
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
create or replace package ut_annotation_cache_manager authid definer as
2+
3+
procedure update_cache(a_object ut_annotated_object, a_cache_id integer);
4+
5+
end;
6+
/
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
create sequence ut_annotation_cache_seq cache 20
2+
/

source/core/annotations/ut_annotation_parser.pkb

Lines changed: 128 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,15 +230,138 @@ create or replace package body ut_annotation_parser as
230230
from table(l_annotations) x
231231
order by x.position;
232232

233-
-- printing out parsed structure for debugging
234-
$if $$ut_trace $then
235-
print_parse_results(l_result);
236-
$end
233+
-- -- printing out parsed structure for debugging
234+
-- $if $$ut_trace $then
235+
-- print_parse_results(l_result);
236+
-- $end
237237
return l_result;
238238
end parse_package_annotations;
239239

240-
------------------------------
240+
function get_post_processed_source(a_source_lines ut_varchar2_rows) return clob is
241+
l_lines sys.dbms_preprocessor.source_lines_t;
242+
l_source clob;
243+
begin
244+
--convert to preprocessor lines
245+
for i in 1 .. a_source_lines.count loop
246+
l_lines(i) := a_source_lines(i);
247+
end loop;
248+
--get post-processed source
249+
l_lines := sys.dbms_preprocessor.get_post_processed_source(l_lines);
250+
--convert to clob
251+
for i in 1..l_lines.count loop
252+
ut_utils.append_to_clob(l_source, replace(l_lines(i), chr(13)||chr(10), chr(10)));
253+
end loop;
254+
return l_source;
255+
end;
256+
257+
------------------------------------------------------------
241258
--public definitions
259+
------------------------------------------------------------
260+
261+
function parse_annotations(a_cursor t_object_sources_cur) return ut_annotated_objects pipelined is
262+
l_rec t_object_source;
263+
l_source clob;
264+
l_annotations ut_annotations;
265+
l_result ut_annotated_object;
266+
ex_package_is_wrapped exception;
267+
pragma exception_init(ex_package_is_wrapped, -24241);
268+
269+
begin
270+
if not a_cursor% isopen then
271+
return;
272+
end if;
273+
loop
274+
275+
fetch a_cursor into l_rec;
276+
exit when a_cursor%notfound;
277+
begin
278+
--convert to post-processed source clob
279+
l_source := get_post_processed_source(l_rec.lines);
280+
--parse annotations
281+
l_annotations := parse_package_annotations(l_source);
282+
dbms_lob.freetemporary(l_source);
283+
exception
284+
when ex_package_is_wrapped then
285+
null;
286+
end;
287+
--convert to query results
288+
l_result := ut_annotated_object( l_rec.owner, l_rec.name, l_rec.type, l_annotations);
289+
290+
ut_annotation_cache_manager.update_cache(l_result, l_rec.cache_id);
291+
if l_annotations is not empty then
292+
pipe row (l_result);
293+
end if;
294+
end loop;
295+
close a_cursor;
296+
return;
297+
end;
298+
299+
300+
function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2) return ut_annotated_objects pipelined is
301+
l_objects_view varchar2(200) := ut_metadata.get_dba_view('dba_objects');
302+
l_sources_view varchar2(200) := ut_metadata.get_dba_view('dba_source');
303+
l_obj ut_annotated_object;
304+
l_current_schema varchar2(250) := ut_utils.ut_owner;
305+
l_cursor sys_refcursor;
306+
l_cursor_sql varchar2(32767);
307+
begin
308+
l_cursor_sql :=
309+
q'[with object_cache_info
310+
as (select /*+ cardinality(i 10000) */ o.owner as object_owner, o.object_name, o.object_type, i.cache_id,
311+
case when o.last_ddl_time < i.parse_time then 'N' else 'Y' end as cache_stale
312+
from ]'||l_objects_view||q'[ o
313+
left join ]'||l_current_schema||q'[.ut_annotation_cache_info i
314+
on o.owner = i.object_owner and o.object_name = i.object_name and o.object_type = i.object_type
315+
where o.object_type = :a_object_type and o.status = 'VALID' and o.owner = :a_object_owner
316+
),
317+
obj_info_with_source
318+
as (select /*+ cardinality(o 10000) */o.object_owner, o.object_name, o.object_type, o.cache_stale, o.cache_id, s.line, s.text
319+
from object_cache_info o
320+
left join ]'||l_sources_view||q'[ s
321+
on s.name = o.object_name
322+
and s.type = :a_object_type
323+
and s.owner = :a_object_owner
324+
and o.cache_stale = 'Y'
325+
),
326+
obj_info_source_grouped
327+
as (select o.object_owner, o.object_name, o.object_type, o.cache_stale, o.cache_id,
328+
cast(collect(o.text order by o.line) as ]'||l_current_schema||q'[.ut_varchar2_rows) as texts
329+
from obj_info_with_source o
330+
group by o.object_owner, o.object_type, o.object_name, o.cache_stale, cache_id
331+
order by o.object_owner, o.object_type, o.object_name
332+
)
333+
select obj
334+
from (
335+
select ]'||l_current_schema||q'[.ut_annotated_object(o.object_owner, o.object_name, o.object_type,
336+
cast(collect(
337+
]'||l_current_schema||q'[.ut_annotation(c.annotation_position, c.annotation_name, c.annotation_text, c.subobject_name) order by c.annotation_position )
338+
as ]'||l_current_schema||q'[.ut_annotations)
339+
) as obj
340+
from obj_info_source_grouped o
341+
join ]'||l_current_schema||q'[.ut_annotation_cache c
342+
on o.cache_id = c.cache_id
343+
where o.cache_stale = 'N'
344+
group by o.object_owner, o.object_name, o.object_type
345+
union all
346+
-- this query needs to be executed as last part of the union
347+
-- as it is updating the cache_stale flag in an autonomous transaction
348+
select value(c) as obj
349+
from table(
350+
]'||l_current_schema||q'[.ut_annotation_parser.parse_annotations(
351+
cursor(select o.object_owner, o.object_name, o.object_type, o.cache_id, o.texts from obj_info_source_grouped o where o.cache_stale = 'Y')
352+
)
353+
) c
354+
) a
355+
order by a.obj.object_owner, a.obj.object_type, a.obj.object_name]';
356+
open l_cursor for l_cursor_sql using a_object_type, a_object_owner, a_object_type, a_object_owner;
357+
loop
358+
fetch l_cursor into l_obj;
359+
exit when l_cursor%notfound;
360+
pipe row (l_obj);
361+
end loop;
362+
close l_cursor;
363+
return;
364+
end;
242365

243366
function get_package_annotations(a_owner_name varchar2, a_name varchar2) return ut_annotations is
244367
l_source clob;

0 commit comments

Comments
 (0)