Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Leah.

Asked: February 08, 2001 - 8:21 pm UTC

Last updated: September 13, 2006 - 6:55 am UTC

Version: 8.1.5

Viewed 10K+ times! This question is

You Asked

During our software build, some database objects
such as packages, views, etc. become invalid after compilation.
You've suggested in one of your q&a's to use dbms_utility.compile_schema to recompile invalid objects in the order of their dependencies. Can you please give an example using the utility for this purpose?

and Tom said...

The goal of the compile schema procedure is to attempt to make valid all INVALID procedures, packages, triggers, views, types, and so on in a schema. This procedure works (in 8.1.6 and before but not 8.1.7) by using the SYS.ORDER_OBJECT_BY_DEPENDENCY view which is supposed to return objects in the order they depend on eachother. If we compile the objects in the order this view returns the data ¡V at the end, all objects should be valid. This procedure runs the ALTER COMPILE command as the user who invoked the procedure (invokers rights).

It should be noted that COMPILE_SCHEMA demands you pass in a case sensitive username ¡V if you call:

scott@TKYTE816> exec dbms_utility.compile_schema( ¡¥scott¡¦ );

It is probable that nothing will happen (unless you have a lower case user named scott. You must pass in SCOTT.

There is however another issue with compile_schema in versions of the database between 8.1.5 to 8.1.6.2 (that is all 8.1.5 and 8.1.6.0 and 8.1.6.1 versions). If you have a Java enabled database, this will introduce some recursive dependencies into your system. This will cause COMPILE_SCHEMA to raise the error:

scott@TKYTE816> exec dbms_utility.compile_schema( user );
BEGIN dbms_utility.compile_schema( user ); END;

*
ERROR at line 1:
ORA-01436: CONNECT BY loop in user data
ORA-06512: at "SYS.DBMS_UTILITY", line 195
ORA-06512: at line 1

If you encoutner this error, we can create our own COMPILE_SCHEMA procedure that behaves exactly as the real COMPILE_SCHEMA. We can do this by compiling the objects in any order we feel like it. It is a common misconception that we must compile objects in some specific order ¡V we can in fact do them in any old order and end up with the same outcome we would have had ordered by dependency. The logic is:

ć Pick any invalid object from a schema that we have not yet tried to compile
ć Compile it
ć Goto step one until there are no more invalid objects that we did not try to compile once

It is that simple ¡V we need no special ordering. That is because a side effect of compiling an invalid object is that all invalid objects it depends on will be compiled in order to validate this one. We just have to keep compiling objects until we have no more invalid ones (well, we might have invalid ones but that would be because they cannot be successfully compiled no matter what). What we might discover is that we need only to compile a SINGLE procedure to have 10 or 20 other objects get compiled. As long as we don¡¦t attempt to manually recompile those 10 or 20 other objects (that would invalidate the first object again) we are OK.

Since the implementation of this procedure is somewhat interesting, we¡¦ll demonstrate it here. We need to rely on an invokers rights routine to do the actual ¡§alter compile¡¨ command ¡V however we need access to the DBA_OBJECTS table to find the ¡§next¡¨ invalid object and report on the status of the just compiled object. We do not want the invoker of the routine necessarily to have to have access to DBA_OBJECTS. In order to achieve this, we will use a mixture of Invokers rights routines and Definers rights routines. We need to make sure that the top level routine, the one called by the end user, is the invokers rights routine however to ensure that roles are enabled and all.

Here is my implementation of a COMPILE_SCHEMA. The person who runs this script must have had SELECT granted to them on the SYS.DBA_OBJECTS view directly (refer to the Invokers Rights chapter for details on why this is). Since this is a SQLPlus script, with some SQLPlus directives in it ¡V I¡¦ll put the script in this time, not the results of actually running the script. I am using a SQLPlus macro variable to fill in the schema name as we compile objects. I am doing this because of the invokers rights routine (need to fully qualify objects if they should always access the SAME table regardless of who is running it) and the fact that I personally do not like to rely on public synonyms. The script will be given to you in pieces below with commentary in between:

column u new_val uname
select user u from dual;

drop table compile_schema_tmp
/

create global temporary table compile_schema_tmp
( object_name varchar2(30),
object_type varchar2(30),
constraint compile_schema_tmp_pk
primary key(object_name,object_type)
)
on commit preserve rows
/
grant all on compile_schema_tmp to public
/

We start the script by getting the currently logged in users username into a SQLPlus macro variable. We will use this later in our create or replace procedures below. The temporary table is used by our procedures to ¡§remember¡¨ what objects we have attempted to compile. We need to use ¡§on commit preserve rows¡¨ because of the fact that we are going to do DDL in our procedure (the alter compile command is DDL) and DDL commits. Next, we can start in on the procedures we need:

create or replace
procedure get_next_object_to_compile( p_username in varchar2,
p_cmd out varchar2,
p_obj out varchar2,
p_typ out varchar2 )
as
begin
select 'alter ' || object_type || ' '
|| p_username || '.' || object_name ||
decode( object_type, 'PACKAGE BODY', ' compile body',
' compile' ), object_name, object_type
into p_cmd, p_obj, p_typ
from dba_objects a
where owner = upper(p_username)
and status = 'INVALID'
and object_type <> 'UNDEFINED'
and not exists ( select null
from compile_schema_tmp b
where a.object_name = b.object_name
and a.object_type = b.object_type
)
and rownum = 1;

insert into compile_schema_tmp
( object_name, object_type )
values
( p_obj, p_typ );
end;
/

This is a definers rights procedure that accesses the DBA_OBJECTS view for us. This will return ¡§some¡¨ invalid object to be compiled ¡V as long as we have not yet attempted to compile it. It just finds the first one. As we retrieve them, we ¡§remember¡¨ them in our temporary table. Next we have our invokers rights routine that will actually do the compilation. This also shows why we needed the ¡§column u new_val uname¡¨ directive above ¡V we need to physically insert the OWNER of the temporary table in here to avoid having to use a synonym. Since we do this dynamically upon install ¡V it makes it better then a synonym:

create or replace procedure compile_schema( p_username in varchar2 )
authid current_user
as
l_cmd varchar2(512);
l_obj dba_objects.object_name%type;
l_typ dba_objects.object_type%type;
begin
delete from &uname..compile_schema_tmp;

loop
get_next_object_to_compile( p_username, l_cmd, l_obj, l_typ );

dbms_output.put_line( l_cmd );
begin
execute immediate l_cmd;
dbms_output.put_line( 'Successful' );
exception
when others then
dbms_output.put_line( sqlerrm );
end;
dbms_output.put_line( chr(9) );
end loop;

exception
when no_data_found then NULL;
end;
/
grant execute on compile_schema to public
/

And that is it ¡V now you can go into any schema that is able to compile some objects and execute:

scott@TKYTE816> exec tkyte.compile_schema('scott')
alter PROCEDURE scott.ANALYZE_MY_TABLES compile
Successful

alter PROCEDURE scott.CUST_LIST compile
ORA-24344: success with compilation error

alter TYPE scott.EMP_MASTER compile
ORA-24344: success with compilation error

alter PROCEDURE scott.FOO compile
Successful

alter PACKAGE scott.LOADLOBS compile
Successful

alter PROCEDURE scott.P compile
Successful

alter PROCEDURE scott.RUN_BY_JOBS compile
Successful

PL/SQL procedure successfully completed.

So, that shows me the objects it attempted to compile and the outcome. According to the above ¡V we compile 7 objects, 2 of which failed and 5 of which succeeded. We compiled them in any order, the order was simply not relevant. This procedure should work in all situations.


Rating

  (6 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Questions

Jeff Yuan, August 19, 2002 - 12:30 pm UTC

After installing 9.2.0.1, I got more than 10 hundred invalid objects in the SYS. I used your method in your wonderful book (and here) to compile the invalid objects, and still got about 150 invalid objects in SYS. Three kinds of errors in my SqlPlus screen:

1. alter TYPE SYS.ODCIARGDESC compile
ORA-02311: cannot alter with COMPILE option a valid type with type or table dependents

Question: How can I manually compile this object? and what cause the problem?

2. alter PACKAGE BODY SYS.DBMS_AQ compile body
ORA-00922: missing or invalid option

Action: "alter package DBMS_AQ compile body;" compiled it.
Question: Is there a bug in your procedure?

3.alter VIEW SYS._ALL_REPCOLUMN_GROUP compile
ORA-00911: invalid character

Action: "alter VIEW "_ALL_REPCOLUMN_GROUP" compile;" compiled it.
Question: Need "" on the name with special chars?

I am more interested in solving the problem in Question 1. Could you please take a look on it? I use 9i rel 2.

Thank you very much.

Jeff Yuan

Tom Kyte
August 19, 2002 - 1:03 pm UTC

> oerr ora 2311
02311, 00000, "cannot alter with COMPILE option a valid type with type or table dependents"
// *Cause: An attempt was made to ALTER with COMPILE option a type that is
// valid and has type or table dependents.
// *Action: No need to perform this operation.


I love that action. I've always wondered why people are so gung ho to have this "perfect" no invalid objects state. I've always just let it fix itself.

Let that type be, it'll fix itself IF and WHEN you need it.


#2 -- looks like there is:

create or replace
procedure get_next_object_to_compile( p_username in varchar2,
p_cmd out varchar2,
p_obj out varchar2,
p_typ out varchar2 )
as
begin
select 'alter ' || object_type || ' '
|| p_username || '.' || object_name ||
decode( object_type, 'PACKAGE BODY', ' compile body',
' compile' ), object_name, object_type
into p_cmd, p_obj, p_typ
from dba_objects a


should be

create or replace
procedure get_next_object_to_compile( p_username in varchar2,
p_cmd out varchar2,
p_obj out varchar2,
p_typ out varchar2 )
as
begin
select 'alter ' ||
decode( object_type, 'PACKAGE BODY', 'PACKAGE', object_type ) || ' '
|| p_username || '.' || object_name ||
decode( object_type, 'PACKAGE BODY', ' compile body',
' compile' ), object_name, object_type
into p_cmd, p_obj, p_typ
from dba_objects a


3) another fix -- need quoted identifiers:


create or replace
procedure get_next_object_to_compile( p_username in varchar2,
p_cmd out varchar2,
p_obj out varchar2,
p_typ out varchar2 )
as
begin
select 'alter ' ||
decode( object_type, 'PACKAGE BODY', 'PACKAGE', object_type ) || ' "'
|| p_username || '"."' || object_name || '" ' ||
decode( object_type, 'PACKAGE BODY', ' compile body',
' compile' ), object_name, object_type
into p_cmd, p_obj, p_typ
from dba_objects a


will do that.

alter type body --> alter type

Jeff Yuan, August 19, 2002 - 2:12 pm UTC

Hi Tom,

Thanks a lot again for your help.

I just found an issue with TYPE BODY. "alter type body xxx compile body;" should be "alter type xxx compile body;".

One example is "alter type XMLGENFORMATTYPE compile body;"

Regards,

Jeff

Another implementation not using temp-tables

Matthias Rogel, March 16, 2004 - 9:05 am UTC

hello tom,

just saw your - nice as always - implementation.

a brute attack not needing get_next_object_to_compile
does the stuff for me since over 3 years now -
notice I just compile my own schema and assume
dependent objects in other schemata don't need to recompile,
but it should be not difficult to overcome this:

create or replace
procedure recompile_invalid_objects is
cursor inv is
SELECT object_type, object_name
FROM user_objects
WHERE STATUS = 'INVALID';
anz integer;
anz_pref integer;
begin
select count(*) into anz
FROM user_objects
WHERE STATUS = 'INVALID';

if anz = 0 then return; end if;
anz_pref := anz + 1;

while anz_pref > anz and anz > 0 loop
for inv_rec in inv loop
if inv_rec.object_type like '% BODY' then
begin
execute immediate 'alter ' || substr(inv_rec.object_type, 1, instr(inv_rec.object_type, ' BODY') - 1) ||
' "' || inv_rec.object_name || '" compile body';
exception when others then null;
end;
else
begin
execute immediate 'alter ' || inv_rec.object_type || ' "' || inv_rec.object_name || '" compile';
exception when others then null;
end;
end if;
end loop;

anz_pref := anz;

select count(*) into anz
FROM user_objects
WHERE STATUS = 'INVALID';


end loop;

if anz > 0 then
declare errm varchar2(2048);
begin
errm := 'The following ' || anz || ' Objects compiled with error:';
for inv_rec in inv loop
if length(errm) < 1950 then
errm := errm || chr(10) || inv_rec.object_name || ' (' || inv_rec.object_type || ')';
else
errm := errm || chr(10) || '...';
exit;
end if;
end loop;
raise_application_error(-20001, errm);
end;
end if;

end recompile_invalid_objects;




Tom Kyte
March 16, 2004 - 9:41 am UTC

it does more work then it needs to (the very first COMPILE very well may have compiled EVERYTHING for you)

And it may well end up with invalid stuff as the very act of compiling something that isn't invalid (because of an implicit compile) will invalidate the things that validated it!!! (catch 22).

I'll stick with

a) pick off the first invalid object you haven't tried to compile yet
b) compile it
c) remember you compiled it (i found a temp table easiest, but an array would work -- especially in 9i with associative arrays)
d) goto a until you get no more.

You might find in a schema with 1000 invalid objects -- you loop once due to the implicit compiles.


can't see 22

Matthias Rogel, March 16, 2004 - 10:38 am UTC

> it does more work then it needs to (the very first COMPILE > very well may have 
> compiled EVERYTHING for you)

true of course, but the procedure is used once a month maybe
and hence, I don't care looking at performance here


> And it may well end up with invalid stuff as the very act of > compiling something 
> that isn't invalid (because of an implicit compile) will > invalidate the things 
> that validated it!!!  (catch 22).

I can't see that (and never experienced it - I really use
the procedure since years )
Could you give me an example ?

SQL> create table temp(t integer);

Tabelle wurde angelegt.
SQL> create view vtemp as select * from temp;

View wurde angelegt.

SQL> create view vvtemp as select * from vtemp;

View wurde angelegt.

SQL> drop table temp;

Tabelle wurde gel÷scht.

SQL> create table temp(t integer);

Tabelle wurde angelegt.

SQL> select object_type,object_name from user_objects where status='INVALID';

OBJECT_TYPE        OBJECT_NAME
------------------ ------------------------------------------------------------------------------
VIEW               VTEMP
VIEW               VVTEMP

SQL> alter view vvtemp compile;

View wurde geõndert.

SQL> select object_type,object_name from user_objects where status='INVALID';

Es wurden keine Zeilen ausgewõhlt

SQL> alter view vtemp compile;

View wurde geõndert.

SQL> select object_type,object_name from user_objects where status='INVALID';

Es wurden keine Zeilen ausgewõhlt


So the last "alter view vtemp compile;" didn't invalidate vvtemp

What do you mean by 
"catch 22" ?

Thank you

 

Tom Kyte
March 16, 2004 - 1:30 pm UTC

i see yours won't but only because it compiles everything over and over.

The last thing DID invalidate it -- you just went back and redid the work again.

I put a dbms_output in there and set up a test case.

V is a view, B depends on V, A depends on B.  I recreate the view V and B and A go invalid, you will:


ops$tkyte@ORA9IR2> exec recompile_invalid_objects;
compiling A
compiling B
compiling A
 
PL/SQL procedure successfully completed.


Now in my method, I would get A, compile it -- which in turn compiles B and so I stop.


You've already said "don't care if it is inefficient" so that is all well and done.  I went with "I'd rather have the stuff fix itself ASAP".

So yes, you can get rid of the table (or an array) -- at a price, too high a price for me.

 

agree

Matthias Rogel, March 17, 2004 - 1:00 am UTC

it just depends on what's more expensive -
temp tables or efficiency
just wanted to show you *can* do it without temp tables

dbms

A reader, September 12, 2006 - 3:30 pm UTC

Tom:

Would you be able to conclude what might be causing this error when moving an app from 8i to 9i.


ORA-20001: Cannot recompile SYS objects
ORA-06512: at "SYS.DBMS_UTILITY", line 145
ORA-06512: at line 1



Tom Kyte
September 13, 2006 - 6:55 am UTC

I would be sorely tempted to utilize support - wouldn't you?

especially when you give someone absolutely zero context to work with - like, well, what procedure you might have been using, the conditions under which the error is raised and so on... things support would collect from you before even looking at the problem.

see Note 225942.1 on metalink, most likely the cause - it is not supported anymore to use the compile schema function on sys.

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library