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

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Next Next commit
Adding trigger solution for annotation parsing.
  • Loading branch information
jgebal committed Apr 17, 2019
commit 96b8480190931defc7752d42e7155055ec893e2e
2 changes: 2 additions & 0 deletions docs/userguide/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ The uninstall process will **not** drop profiler tables, as they can potentially

It is up to DBA to maintain the storage of the profiler tables.

Additionally the user performing the installation must have a `ADMINISTER DATABASE TRIGGER` privilege. This is required for installation of trigger that is responsible for parsing annotations at at compile-time of a package.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought that trigger will be an optional to install ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optionality is on a TO-DO list for this feature.
I wanted to have it reviewed.


# Manual installation procedure

### Creating schema for utPLSQL
Expand Down
6 changes: 4 additions & 2 deletions source/check_sys_grants.sql
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
declare
c_expected_grants constant dbmsoutput_linesarray
:= dbmsoutput_linesarray(
'CREATE TYPE','CREATE VIEW','CREATE SYNONYM','CREATE SEQUENCE','CREATE PROCEDURE','CREATE TABLE'
'CREATE TYPE','CREATE VIEW','CREATE SYNONYM','CREATE SEQUENCE','CREATE PROCEDURE','CREATE TABLE', 'ADMINISTER DATABASE TRIGGER'
);

l_expected_grants dbmsoutput_linesarray := c_expected_grants;
l_missing_grants varchar2(4000);
begin
if user != SYS_CONTEXT('userenv','current_schema') then
for i in 1 .. l_expected_grants.count loop
l_expected_grants(i) := replace(l_expected_grants(i),' ',' ANY ');
if l_expected_grants(i) != 'ADMINISTER DATABASE TRIGGER' then
l_expected_grants(i) := replace(l_expected_grants(i),' ',' ANY ');
end if;
end loop;
end if;
select listagg(' - '||privilege,CHR(10)) within group(order by privilege)
Expand Down
2 changes: 1 addition & 1 deletion source/core/annotations/ut_annotation_cache_info.sql
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ create table ut_annotation_cache_info (
object_type varchar2(250) not null,
parse_time timestamp not null,
constraint ut_annotation_cache_info_pk primary key(cache_id),
constraint ut_annotation_cache_info_uk unique (object_owner, object_name, object_type)
constraint ut_annotation_cache_info_uk unique (object_owner, object_type, object_name)
) organization index;

73 changes: 56 additions & 17 deletions source/core/annotations/ut_annotation_cache_manager.pkb
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,40 @@ create or replace package body ut_annotation_cache_manager as

procedure update_cache(a_object ut_annotated_object) is
l_cache_id integer;
l_new_objects_count integer := 0;
pragma autonomous_transaction;
begin
update ut_annotation_cache_info i
set i.parse_time = systimestamp
where (i.object_owner, i.object_name, i.object_type)
in ((a_object.object_owner, a_object.object_name, a_object.object_type))
returning cache_id into l_cache_id;
if sql%rowcount = 0 then
insert into ut_annotation_cache_info
(cache_id, object_owner, object_name, object_type, parse_time)
values (ut_annotation_cache_seq.nextval, a_object.object_owner, a_object.object_name, a_object.object_type, systimestamp)
-- if not in trigger, or object has annotations
if ora_sysevent is null or a_object.annotations is not null and a_object.annotations.count > 0 then

update ut_annotation_cache_info i
set i.parse_time = systimestamp
where (i.object_owner, i.object_name, i.object_type)
in ((a_object.object_owner, a_object.object_name, a_object.object_type))
returning cache_id into l_cache_id;

if sql%rowcount = 0 then

insert into ut_annotation_cache_info
(cache_id, object_owner, object_name, object_type, parse_time)
values (ut_annotation_cache_seq.nextval, a_object.object_owner, a_object.object_name, a_object.object_type, systimestamp)
returning cache_id into l_cache_id;
l_new_objects_count := 1;
end if;

end if;

delete from ut_annotation_cache c
where cache_id = l_cache_id;
update ut_annotation_cache_schema s
Comment thread
jgebal marked this conversation as resolved.
set s.object_count = s.object_count + l_new_objects_count, s.max_parse_time = systimestamp
where s.object_type = a_object.object_type and s.object_owner = a_object.object_owner;

if sql%rowcount = 0 then
insert into ut_annotation_cache_schema s
(object_owner, object_type, object_count, max_parse_time)
values (a_object.object_owner, a_object.object_type, l_new_objects_count, systimestamp);
end if;

delete from ut_annotation_cache c where cache_id = l_cache_id;

if a_object.annotations is not null and a_object.annotations.count > 0 then
insert into ut_annotation_cache
Expand Down Expand Up @@ -73,17 +91,32 @@ create or replace package body ut_annotation_cache_manager as
commit;
end;

function get_cache_schema_info(a_object_owner varchar2, a_object_type varchar2) return t_cache_schema_info is
l_result t_cache_schema_info;
begin
begin
select *
into l_result
from ut_annotation_cache_schema s
where s.object_type = a_object_type and s.object_owner = a_object_owner;
exception
when no_data_found then
null;
end;
return l_result;
end;

procedure remove_from_cache(a_objects ut_annotation_objs_cache_info) is
pragma autonomous_transaction;
begin

delete from ut_annotation_cache_info i
where exists (
select 1 from table (a_objects) o
where o.object_name = i.object_name
and o.object_type = i.object_type
and o.object_owner = i.object_owner
);
where exists (
select 1 from table (a_objects) o
where o.object_name = i.object_name
and o.object_type = i.object_type
and o.object_owner = i.object_owner
);

commit;
end;
Expand Down Expand Up @@ -139,6 +172,12 @@ create or replace package body ut_annotation_cache_manager as
delete from ut_annotation_cache_info i
where ' || l_filter
using a_object_owner, a_object_type;

execute immediate '
delete from ut_annotation_cache_schema s
where ' || l_filter
using a_object_owner, a_object_type;

commit;
end;

Expand Down
4 changes: 3 additions & 1 deletion source/core/annotations/ut_annotation_cache_manager.pks
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ create or replace package ut_annotation_cache_manager authid definer as
See the License for the specific language governing permissions and
limitations under the License.
*/

subtype t_cache_schema_info is ut_annotation_cache_schema%rowtype;
/**
* Populates cache with information about object and it's annotations
* Cache information for individual object is modified by this code
Expand All @@ -34,6 +34,8 @@ create or replace package ut_annotation_cache_manager authid definer as
*/
function get_annotations_for_objects(a_cached_objects ut_annotation_objs_cache_info, a_parse_time timestamp) return sys_refcursor;

function get_cache_schema_info(a_object_owner varchar2, a_object_type varchar2) return t_cache_schema_info;

/**
* Removes cached information about annotations for objects on the list and updates parse_time in cache info table.
*
Expand Down
21 changes: 21 additions & 0 deletions source/core/annotations/ut_annotation_cache_schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
create table ut_annotation_cache_schema (
/*
utPLSQL - Version 3
Copyright 2016 - 2017 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
object_owner varchar2(250) not null,
object_type varchar2(250) not null,
object_count integer not null,
max_parse_time date not null,
constraint ut_annotation_cache_schema_pk primary key(object_owner, object_type)
) organization index;

99 changes: 78 additions & 21 deletions source/core/annotations/ut_annotation_manager.pkb
Original file line number Diff line number Diff line change
Expand Up @@ -58,26 +58,39 @@ create or replace package body ut_annotation_manager as
l_cursor_text varchar2(32767);
l_result ut_annotation_objs_cache_info;
begin
l_cursor_text :=
q'[select ]'||l_ut_owner||q'[.ut_annotation_obj_cache_info(
object_owner => o.owner,
object_name => o.object_name,
object_type => o.object_type,
needs_refresh => case when o.last_ddl_time < cast(i.parse_time as date) then 'N' else 'Y' end
)
from ]'||l_objects_view||q'[ o
left join ]'||l_ut_owner||q'[.ut_annotation_cache_info i
on o.owner = i.object_owner
and o.object_name = i.object_name
and o.object_type = i.object_type
where o.owner = ']'||a_object_owner||q'['
and o.object_type = ']'||a_object_type||q'['
and ]'
|| case
when a_parse_date is null
then ':a_parse_date is null'
else 'o.last_ddl_time >= cast(:a_parse_date as date)'
end;
if ut_trigger_check.is_alive() then
l_cursor_text :=
q'[select ]'||l_ut_owner||q'[.ut_annotation_obj_cache_info(
object_owner => i.object_owner,
object_name => i.object_name,
object_type => i.object_type,
needs_refresh => 'N'
)
from ]'||l_ut_owner||q'[.ut_annotation_cache_info i
where i.object_owner = :a_object_owner
and i.object_type = :a_object_type]';
else
l_cursor_text :=
q'[select ]'||l_ut_owner||q'[.ut_annotation_obj_cache_info(
object_owner => o.owner,
object_name => o.object_name,
object_type => o.object_type,
needs_refresh => case when o.last_ddl_time < cast(i.parse_time as date) then 'N' else 'Y' end
)
from ]'||l_objects_view||q'[ o
left join ]'||l_ut_owner||q'[.ut_annotation_cache_info i
on o.owner = i.object_owner
and o.object_name = i.object_name
and o.object_type = i.object_type
where o.owner = ']'||a_object_owner||q'['
and o.object_type = ']'||a_object_type||q'['
and ]'
|| case
when a_parse_date is null
then ':a_parse_date is null'
else 'o.last_ddl_time >= cast(:a_parse_date as date)'
end;
end if;
open l_rows for l_cursor_text using a_parse_date;
fetch l_rows bulk collect into l_result limit 1000000;
close l_rows;
Expand Down Expand Up @@ -222,11 +235,55 @@ create or replace package body ut_annotation_manager as
);
end;

procedure trigger_obj_annotation_rebuild is
l_sql_text ora_name_list_t;
l_parts binary_integer;
l_object_to_parse ut_annotation_obj_cache_info;

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_result sys_refcursor;
l_sql_text ora_name_list_t := a_sql_text;
begin
if a_parts > 0 then
l_sql_text(1) := regexp_replace(l_sql_text(1),'^\s*create(\s+or\s+replace)?\s+', modifier => 'i');
for i in 1..a_parts loop
ut_utils.append_to_clob(l_sql_clob, l_sql_text(i));
end loop;
l_sql_lines := ut_utils.convert_collection( ut_utils.clob_to_table(l_sql_clob) );
end if;
open l_result for
select a_object_name as name, column_value||chr(10) as text from table(l_sql_lines);
return l_result;
end;
begin
ut_trigger_check.is_alive();

if ora_dict_obj_type = 'PACKAGE' then

l_object_to_parse := ut_annotation_obj_cache_info(ora_dict_obj_owner, ora_dict_obj_name, ora_dict_obj_type, 'Y');

if ora_sysevent = 'CREATE' then
l_parts := ORA_SQL_TXT(l_sql_text);
build_annot_cache_for_sources(
ora_dict_obj_owner, ora_dict_obj_type,
get_source_from_sql_text(ora_dict_obj_name, l_sql_text, l_parts)
);
elsif ora_sysevent = 'ALTER' then
null;
--update parse_time
elsif ora_sysevent = 'DROP' then
ut_annotation_cache_manager.remove_from_cache(ut_annotation_objs_cache_info(l_object_to_parse));
end if;
end if;
end;

function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_parse_date timestamp := null) return ut_annotated_objects pipelined is
l_info_rows ut_annotation_objs_cache_info;
l_cursor sys_refcursor;
l_results ut_annotated_objects;
c_object_fetch_limit constant integer := 10;
c_object_fetch_limit constant integer := 10;
begin

l_info_rows := get_annotation_objs_info(a_object_owner, a_object_type, a_parse_date);
Expand Down
5 changes: 5 additions & 0 deletions source/core/annotations/ut_annotation_manager.pks
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ create or replace package ut_annotation_manager authid current_user as
*/
procedure rebuild_annotation_cache(a_object_owner varchar2, a_object_type varchar2);

/**
* Rebuilds annotation cache for a specified object.
*/
procedure trigger_obj_annotation_rebuild;

/**
* Removes cached information about annotations for objects of specified type and specified owner
*
Expand Down
15 changes: 15 additions & 0 deletions source/core/annotations/ut_trigger_annotation_parsing.trg
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
create or replace trigger ut_trigger_annotation_parsing
after create or alter or drop
on database
begin
if ora_dict_obj_type = 'PACKAGE'
or (ora_dict_obj_owner = UPPER('&&UT3_OWNER')
and ora_dict_obj_name = 'UT3_TRIGGER_ALIVE'
and ora_dict_obj_type = 'SYNONYM')
then
execute immediate 'begin ut_annotation_manager.trigger_obj_annotation_rebuild; end;';
Comment thread
lwasylow marked this conversation as resolved.
Outdated
end if;
exception
when others then null;
end;
/
40 changes: 40 additions & 0 deletions source/core/annotations/ut_trigger_check.pkb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
create or replace package body ut_trigger_check is
/*
utPLSQL - Version 3
Copyright 2016 - 2018 utPLSQL Project

Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

g_is_trigger_live boolean := false;

function is_alive return boolean is
pragma autonomous_transaction;
l_ut_owner varchar2(250) := ut_utils.ut_owner;
l_is_trigger_live boolean;
begin
execute immediate 'create or replace synonym '||l_ut_owner||'.ut3_trigger_alive for no_object';
l_is_trigger_live := g_is_trigger_live;
g_is_trigger_live := false;
return l_is_trigger_live;
end;

procedure is_alive is
begin
if ora_dict_obj_owner = 'UT3' and ora_dict_obj_name = 'UT3_TRIGGER_TEST' and ora_dict_obj_type = 'SYNONYM' then
g_is_trigger_live := true;
end if;
end;

end;
/
28 changes: 28 additions & 0 deletions source/core/annotations/ut_trigger_check.pks
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
create or replace package ut_trigger_check authid definer is
/*
utPLSQL - Version 3
Copyright 2016 - 2018 utPLSQL Project

Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/**
* checks if the trigger &&UT3_OWNER._PARSE is enabled and operational.
*/

function is_alive return boolean;

procedure is_alive;

end;
/
Loading