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

Skip to content

Commit 94a1c23

Browse files
committed
Updated readme for installation with trigger.
Added additional tests for annotation management with / without trigger. Fixed code for situations when trigger gets added on pre-existing tests.
1 parent a173b38 commit 94a1c23

11 files changed

Lines changed: 358 additions & 160 deletions

docs/userguide/install.md

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,31 @@ The utPLSQL may be installed on any supported version of Oracle Database [see](h
7272
* 12c
7373
* 12c R2
7474
* 18c
75+
* 19c
7576

7677
# Headless installation
7778

78-
To install the utPLSQL into a new database schema and grant it to public, execute the script `install_headless.sql` as SYSDBA.
79+
utPLSQL can be installed with DDL trigger, to enable tracking of DDL changes to your unit test packages.
80+
This is the recommended installation approach, when you want to compile and run unit test packages in a schema containing huge amount of database packages (for example Oracle EBS installation schema).
81+
The reason for having DDL trigger is to enable in-time annotation parsing for utPLSQL.
82+
Without DDL trigger, utPLSQL needs to investigate your schema objects last_ddl_timestamp each time tests are executed to check if any of DB packages were changed in given schema and if they need scanning for annotation changes.
83+
This process can be time-consuming if DB schema is large.
7984

80-
The script accepts three optional parameters that define:
85+
The headless scripts accept three optional parameters that define:
8186
- username to create as owner of utPLSQL (default `ut3`)
8287
- password for owner of utPLSQL (default `XNtxj8eEgA6X6b6f`)
8388
- tablespace to use for storage of profiler data (default `users`)
8489

90+
The scripts need to be executed by `SYSDBA`, in order to grant access to `DBMS_LOCK` and `DBMS_CRYPTO` system packages.
91+
92+
*Note:* Grant on `DBMS_LOCK` is required only for installation on Oracle versions below 18c. For versions 18c and above, utPLSQL uses `DBMS_SESSION.SLEEP` so access to `DBMS_LOCK` package is no longer needed.
93+
94+
*Note:* The user performing the installation must have the `ADMINISTER DATABASE TRIGGER` privilege. This is required for installation of trigger that is responsible for parsing annotations at at compile-time of a package.
95+
96+
## Installation without DDL trigger
97+
98+
To install the utPLSQL into a new database schema and grant it to public, execute the script `install_headless.sql` as SYSDBA.
99+
85100
Example invocation of the script from command line:
86101
```bash
87102
cd source
@@ -94,16 +109,37 @@ cd source
94109
sqlplus sys/sys_pass@db as sysdba @install_headless.sql utp3 my_verySecret_password utp3_tablespace
95110
```
96111

97-
The script needs to be executed by `SYSDBA`, in order to grant access to `DBMS_LOCK` and `DBMS_CRYPTO` system packages.
112+
## Installation with DDL trigger
113+
114+
To install the utPLSQL into a new database schema and grant it to public, execute the script `install_headless_with_trigger.sql` as SYSDBA.
115+
116+
Example invocation of the script from command line:
117+
```bash
118+
cd source
119+
sqlplus sys/sys_pass@db as sysdba @install_headless_with_trigger.sql
120+
```
121+
122+
Invoking script with parameters:
123+
```bash
124+
cd source
125+
sqlplus sys/sys_pass@db as sysdba @install_headless_with_trigger.sql utp3 my_verySecret_password utp3_tablespace
126+
```
127+
128+
*Note:*
98129

99-
*Note:* Grant on `DBMS_LOCK` is required on Oracle versions below 18c
130+
When installing utPLSQL into database with existing unit test packages, utPLSQL will not be able to already-existing unit test packages.
131+
When utPSLQL was installed with DDL trigger, you have to do one of:
132+
- Recompile existing Unit Test packages to make utPLSQL aware of their existence
133+
- Invoke `exec ut_runner.rebuild_annotation_cache(a_schema_name=> ... );` for every schema containing unit tests in your database
100134

135+
Steps above are required to assure annotation cache is populated properly from existing objects.
136+
Rebuilding annotation cache might be faster than code recompilation.
101137

102138
# Recommended Schema
103139
It is highly recommended to install utPLSQL in it's own schema. You are free to choose any name for this schema.
104140
Installing uPLSQL into shared schema is really not recommended as you loose isolation of framework.
105141

106-
If the installation and utPLSQL owner user is one and the same, the user must have the following Oracle system permissions before you can proceed with the installation.
142+
If the installing user and utPLSQL owner is one and the same, the user must have the following Oracle system permissions before you can proceed with the installation.
107143

108144
- CREATE SESSION
109145
- CREATE PROCEDURE
@@ -113,6 +149,7 @@ If the installation and utPLSQL owner user is one and the same, the user must ha
113149
- CREATE VIEW
114150
- CREATE SYNONYM
115151
- ALTER SESSION
152+
- CREATE TRIGGER
116153

117154
In addition the user must be granted the execute privilege on `DBMS_LOCK` and `DBMS_CRYPTO` packages.
118155

@@ -121,11 +158,9 @@ The uninstall process will **not** drop profiler tables, as they can potentially
121158

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

124-
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.
125-
126161
# Manual installation procedure
127162

128-
### Creating schema for utPLSQL
163+
## Creating schema for utPLSQL
129164
To create the utPLSQL schema and grant all the required privileges execute script `create_utplsql_owner.sql` from the `source` directory with parameters:
130165

131166
- `user name` - the name of the user that will own of utPLSQL object
@@ -138,8 +173,8 @@ cd source
138173
sqlplus sys/sys_password@database as sysdba @create_utPLSQL_owner.sql ut3 ut3 users
139174
```
140175

141-
### Installing utPLSQL
142-
To install the utPLSQL framework into your database run the `/source/install.sql` script and provide `schema_name` where utPLSQL is to be installed.
176+
## Installing utPLSQL
177+
To install the utPLSQL framework into your database, go to `source` directory, run the `install.sql` providing the `schema_name` for utPLSQL as parameter.
143178
Schema must be created prior to calling the `install` script.
144179
You may install utPLSQL from any account that has sufficient privileges to create objects in other users schema.
145180

@@ -149,7 +184,25 @@ cd source
149184
sqlplus admin/admins_password@database @install.sql ut3
150185
```
151186

152-
### Allowing other users to access the utPLSQL framework
187+
## Installing DDL trigger
188+
To minimize startup time of utPLSQL framework (especially on a database with large schema) it is recommended to install utPLSQL DDL trigger to enable utPLSQL annotation to be updated at compile-time.
189+
190+
It's recommended to install DDL trigger when connected as `SYSDBA` user. Trigger is created in utPLSQL schema.
191+
If using the owner schema of utPLSQL to install trigger, the owner needs to have `ADMINISTER DATABASE TRIGGER` and `CREATE TRIGGER` system privileges.
192+
If using different user to install trigger, the user needs to have `ADMINISTER DATABASE TRIGGER` and `CREATE ANY TRIGGER` system privileges.
193+
194+
To install DDL trigger go to `source` directory, run the `install_ddl_trigger.sql` providing the `schema_name` for utPLSQL as parameter.
195+
196+
Example invocation:
197+
```bash
198+
cd source
199+
sqlplus admin/admins_password@database @install_ddl_trigger.sql ut3
200+
```
201+
202+
*Note:* Trigger can be installed ant any point in time.
203+
204+
205+
## Allowing other users to access the utPLSQL framework
153206
In order to allow other users to access utPLSQL, synonyms must be created and privileges granted.
154207
You have two options:
155208

source/check_sys_grants.sql

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1+
DEFINE expected_grants = "&1"
12
declare
2-
c_expected_grants constant dbmsoutput_linesarray
3-
:= dbmsoutput_linesarray(
4-
'CREATE TYPE','CREATE VIEW','CREATE SYNONYM','CREATE SEQUENCE','CREATE PROCEDURE','CREATE TABLE', 'ADMINISTER DATABASE TRIGGER'
5-
);
3+
c_expected_grants constant dbmsoutput_linesarray := dbmsoutput_linesarray( &expected_grants );
64

75
l_expected_grants dbmsoutput_linesarray := c_expected_grants;
86
l_missing_grants varchar2(4000);

source/core/annotations/ut_annotation_manager.pkb

Lines changed: 57 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -51,30 +51,34 @@ create or replace package body ut_annotation_manager as
5151
return l_result;
5252
end;
5353

54-
function get_annotation_objs_info(a_object_owner varchar2, a_object_type varchar2, a_parse_date timestamp := null) return ut_annotation_objs_cache_info is
55-
l_rows sys_refcursor;
56-
l_ut_owner varchar2(250) := ut_utils.ut_owner;
57-
l_objects_view varchar2(200) := ut_metadata.get_objects_view_name();
58-
l_cursor_text varchar2(32767);
59-
l_result ut_annotation_objs_cache_info;
60-
l_object_owner varchar2(250);
61-
l_object_type varchar2(250);
54+
function get_annotation_objs_info(
55+
a_object_owner varchar2,
56+
a_object_type varchar2,
57+
a_parse_date timestamp := null,
58+
a_full_scan boolean := true
59+
) return ut_annotation_objs_cache_info is
60+
l_rows sys_refcursor;
61+
l_ut_owner varchar2(250) := ut_utils.ut_owner;
62+
l_objects_view varchar2(200) := ut_metadata.get_objects_view_name();
63+
l_cursor_text varchar2(32767);
64+
l_result ut_annotation_objs_cache_info;
65+
l_object_owner varchar2(250);
66+
l_object_type varchar2(250);
6267
begin
6368
ut_event_manager.trigger_event(
64-
'get_annotation_objs_info - start ( ut_trigger_check.is_alive = '
65-
|| case when ut_trigger_check.is_alive() then 'Y' else 'N' end || ' )'
69+
'get_annotation_objs_info - start ( a_full_scan = ' || ut_utils.to_string(a_full_scan) || ' )'
6670
);
67-
if ut_trigger_check.is_alive() then
71+
if not a_full_scan then
6872
l_cursor_text :=
6973
q'[select ]'||l_ut_owner||q'[.ut_annotation_obj_cache_info(
70-
object_owner => i.object_owner,
71-
object_name => i.object_name,
72-
object_type => i.object_type,
74+
object_owner => i.object_owner,
75+
object_name => i.object_name,
76+
object_type => i.object_type,
7377
needs_refresh => 'N'
7478
)
7579
from ]'||l_ut_owner||q'[.ut_annotation_cache_info i
7680
where i.object_owner = :a_object_owner
77-
and i.object_type = :a_object_type]';
81+
and i.object_type = :a_object_type]';
7882
open l_rows for l_cursor_text using a_object_owner, a_object_type;
7983
else
8084
if a_object_owner is not null then
@@ -85,17 +89,17 @@ create or replace package body ut_annotation_manager as
8589
end if;
8690
l_cursor_text :=
8791
q'[select ]'||l_ut_owner||q'[.ut_annotation_obj_cache_info(
88-
object_owner => o.owner,
89-
object_name => o.object_name,
90-
object_type => o.object_type,
92+
object_owner => o.owner,
93+
object_name => o.object_name,
94+
object_type => o.object_type,
9195
needs_refresh => case when o.last_ddl_time < cast(i.parse_time as date) then 'N' else 'Y' end
9296
)
9397
from ]'||l_objects_view||q'[ o
9498
left join ]'||l_ut_owner||q'[.ut_annotation_cache_info i
95-
on o.owner = i.object_owner
99+
on o.owner = i.object_owner
96100
and o.object_name = i.object_name
97101
and o.object_type = i.object_type
98-
where o.owner = ']'||l_object_owner||q'['
102+
where o.owner = ']'||l_object_owner||q'['
99103
and o.object_type = ']'||l_object_type||q'['
100104
and ]'
101105
|| case
@@ -105,7 +109,7 @@ create or replace package body ut_annotation_manager as
105109
end;
106110
open l_rows for l_cursor_text using a_parse_date;
107111
end if;
108-
fetch l_rows bulk collect into l_result limit 1000000;
112+
fetch l_rows bulk collect into l_result limit 10000000;
109113
close l_rows;
110114
ut_event_manager.trigger_event('get_annotation_objs_info - end (count='||l_result.count||')');
111115
return l_result;
@@ -207,14 +211,15 @@ create or replace package body ut_annotation_manager as
207211
end;
208212

209213

210-
procedure rebuild_annotation_cache(
214+
procedure refresh_annotation_cache(
211215
a_object_owner varchar2,
212216
a_object_type varchar2,
213217
a_info_rows ut_annotation_objs_cache_info
214218
) is
215219
l_objects_to_parse ut_annotation_objs_cache_info;
216220
begin
217-
select value(x)bulk collect into l_objects_to_parse
221+
select value(x)
222+
bulk collect into l_objects_to_parse
218223
from table(a_info_rows) x where x.needs_refresh = 'Y';
219224

220225
ut_event_manager.trigger_event('rebuild_annotation_cache - start (l_objects_to_parse.count = '||l_objects_to_parse.count||')');
@@ -243,12 +248,10 @@ create or replace package body ut_annotation_manager as
243248
--public definitions
244249
------------------------------------------------------------
245250
procedure rebuild_annotation_cache(a_object_owner varchar2, a_object_type varchar2) is
251+
l_annotation_objs_info ut_annotation_objs_cache_info;
246252
begin
247-
rebuild_annotation_cache(
248-
a_object_owner,
249-
a_object_type,
250-
get_annotation_objs_info(a_object_owner, a_object_type, null)
251-
);
253+
l_annotation_objs_info := get_annotation_objs_info(a_object_owner, a_object_type, null, true);
254+
refresh_annotation_cache( a_object_owner, a_object_type, l_annotation_objs_info );
252255
end;
253256

254257
procedure trigger_obj_annotation_rebuild is
@@ -273,9 +276,23 @@ create or replace package body ut_annotation_manager as
273276
select a_object_name as name, column_value||chr(10) as text from table(l_sql_lines);
274277
return l_result;
275278
end;
276-
begin
277-
ut_trigger_check.is_alive();
278279

280+
function get_source_for_object(a_object_owner varchar2, a_object_name varchar2, a_object_type varchar2) return sys_refcursor is
281+
l_result sys_refcursor;
282+
l_sources_view varchar2(200) := ut_metadata.get_source_view_name();
283+
begin
284+
open l_result for
285+
q'[select :a_object_name, s.text
286+
from ]'||l_sources_view||q'[ s
287+
where s.type = :a_object_type
288+
and s.owner = :a_object_owner
289+
and s.name = :a_object_name
290+
order by s.line]'
291+
using a_object_name, a_object_type, a_object_owner, a_object_name;
292+
return l_result;
293+
end;
294+
295+
begin
279296
if ora_dict_obj_type in ('PACKAGE','PROCEDURE','FUNCTION','TYPE') then
280297

281298
l_object_to_parse := ut_annotation_obj_cache_info(ora_dict_obj_owner, ora_dict_obj_name, ora_dict_obj_type, 'Y');
@@ -287,27 +304,29 @@ create or replace package body ut_annotation_manager as
287304
get_source_from_sql_text(ora_dict_obj_name, l_sql_text, l_parts)
288305
);
289306
elsif ora_sysevent = 'ALTER' then
290-
null;
291-
--update parse_time
307+
build_annot_cache_for_sources(
308+
ora_dict_obj_owner, ora_dict_obj_type,
309+
get_source_for_object(ora_dict_obj_owner, ora_dict_obj_name, ora_dict_obj_type)
310+
);
292311
elsif ora_sysevent = 'DROP' then
293312
ut_annotation_cache_manager.remove_from_cache(ut_annotation_objs_cache_info(l_object_to_parse));
294313
end if;
295314
end if;
296315
end;
297316

298317
function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_parse_date timestamp := null) return ut_annotated_objects pipelined is
299-
l_info_rows ut_annotation_objs_cache_info;
318+
l_annotation_objs_info ut_annotation_objs_cache_info;
300319
l_cursor sys_refcursor;
301320
l_results ut_annotated_objects;
302321
c_object_fetch_limit constant integer := 10;
322+
l_full_scan_needed boolean := not ut_trigger_check.is_alive();
303323
begin
304324
ut_event_manager.trigger_event('get_annotated_objects - start');
305-
306-
l_info_rows := get_annotation_objs_info(a_object_owner, a_object_type, a_parse_date);
307-
rebuild_annotation_cache(a_object_owner, a_object_type, l_info_rows);
325+
l_annotation_objs_info := get_annotation_objs_info(a_object_owner, a_object_type, a_parse_date, l_full_scan_needed);
326+
refresh_annotation_cache(a_object_owner, a_object_type, l_annotation_objs_info);
308327

309328
--pipe annotations from cache
310-
l_cursor := ut_annotation_cache_manager.get_annotations_for_objects(l_info_rows, a_parse_date);
329+
l_cursor := ut_annotation_cache_manager.get_annotations_for_objects(l_annotation_objs_info, a_parse_date);
311330
loop
312331
fetch l_cursor bulk collect into l_results limit c_object_fetch_limit;
313332
for i in 1 .. l_results.count loop
@@ -357,5 +376,6 @@ create or replace package body ut_annotation_manager as
357376
end if;
358377
return l_result;
359378
end;
379+
360380
end ut_annotation_manager;
361381
/

source/core/annotations/ut_annotation_manager.pks

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@ create or replace package ut_annotation_manager authid current_user as
5656
*/
5757
procedure purge_cache(a_object_owner varchar2, a_object_type varchar2);
5858

59-
59+
60+
/*
61+
* Returns a hash value of suitepath based on input path and random seed
62+
*/
6063
function hash_suite_path(a_path varchar2, a_random_seed positiven) return varchar2;
6164

6265
end ut_annotation_manager;

source/core/annotations/ut_trigger_annotation_parsing.trg

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@ create or replace trigger ut_trigger_annotation_parsing
22
after create or alter or drop
33
on database
44
begin
5-
if ( ora_dict_obj_type in ('PACKAGE','PROCEDURE','FUNCTION','TYPE')
5+
if (ora_dict_obj_owner = UPPER('&&UT3_OWNER')
6+
and ora_dict_obj_name = 'UT3_TRIGGER_ALIVE'
7+
and ora_dict_obj_type = 'SYNONYM')
8+
then
9+
execute immediate 'begin ut_trigger_check.is_alive(); end;';
10+
elsif ( ora_dict_obj_type in ('PACKAGE','PROCEDURE','FUNCTION','TYPE')
611
and ora_dict_obj_owner
712
not in (
813
'ANONYMOUS','APPQOSSYS','AUDSYS','DBSFWUSER','DBSNMP','DIP','GGSYS','GSMADMIN_INTERNAL',
914
'GSMCATUSER','GSMUSER','ORACLE_OCM','OUTLN','REMOTE_SCHEDULER_AGENT','SYS','SYS$UMF',
1015
'SYSBACKUP','SYSDG','SYSKM','SYSRAC','SYSTEM','WMSYS','XDB','XS$NULL')
1116
and not (ora_dict_obj_type = 'TYPE' and ora_dict_obj_name like 'SYS\_PLSQL\_%' escape '\')
1217
)
13-
or (ora_dict_obj_owner = UPPER('&&UT3_OWNER')
14-
and ora_dict_obj_name = 'UT3_TRIGGER_ALIVE'
15-
and ora_dict_obj_type = 'SYNONYM')
1618
then
1719
execute immediate 'begin ut_annotation_manager.trigger_obj_annotation_rebuild; end;';
1820
end if;

source/core/annotations/ut_trigger_check.pkb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ create or replace package body ut_trigger_check is
2929

3030
procedure is_alive is
3131
begin
32-
if ora_dict_obj_owner = ut_utils.ut_owner and ora_dict_obj_name = gc_check_object_name and ora_dict_obj_type = 'SYNONYM' then
32+
if ora_dict_obj_owner is not null and ora_dict_obj_name is not null and ora_dict_obj_type is not null then
3333
g_is_trigger_live := true;
3434
else
3535
g_is_trigger_live := false;

source/core/annotations/ut_trigger_check.pks

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ create or replace package ut_trigger_check authid definer is
2424
function is_alive return boolean;
2525

2626
/**
27-
* If called from a DDL trigger when creating object gc_check_object_name, sts alive flag to true
27+
* If called from a DDL trigger when creating object gc_check_object_name, sets alive flag to true
2828
* Otherwise sets alive flag to false.
2929
*/
3030
procedure is_alive;

source/install.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ prompt &&line_separator
3131
alter session set current_schema = &&ut3_owner;
3232

3333
@@check_object_grants.sql
34-
@@check_sys_grants.sql
34+
@@check_sys_grants.sql "'CREATE TYPE','CREATE VIEW','CREATE SYNONYM','CREATE SEQUENCE','CREATE PROCEDURE','CREATE TABLE'"
3535
--set define off
3636

3737
--dbms_output buffer cache table

0 commit comments

Comments
 (0)