Showing posts with label procedure. Show all posts
Showing posts with label procedure. Show all posts

Thursday, January 31, 2013

Why is my Package or Procedure Invalid?

I had a problem earlier this week with a package which kept going INVALID. Once I had worked out the reason, I decided to do a worked example of it on Oracle 11.2.0.2.7. I created users called SOMEBODY, JLS and CBS. JLS has nothing to do with the pop group of the same name and CBS has nothing to do with the news network. You will notice that they have some unusual permissions. This is to save me having to connect as SYS later in the example. It has nothing to do with the problem:

conn / as sysdba
Connected.
SQL> create user somebody
  2  identified by somebody
  3  default tablespace users
  4  quota unlimited on users
  5  /

User created.
 
SQL> grant create session, create table to somebody
  2  /
 
Grant succeeded.
 
SQL> create user jls
  2  identified by jls
  3  default tablespace users
  4  quota unlimited on users
  5  /
 
User created.
 
SQL> grant
  2  create session,
  3  create procedure,
  4  update any table,
  5  select any table,
  6  select any dictionary to jls
  7  /
 
Grant succeeded.
 
SQL> create user cbs
  2  identified by cbs
  3  default tablespace users
  4  quota unlimited on users
  5  /
 
User created.
 
SQL> grant
  2  create session,
  3  create table,
  4  select any dictionary,
  5  create public synonym to cbs
  6  /
 
Grant succeeded.

SQL>

SOMEBODY created a table called SITE:
 
SQL> conn somebody/somebody
Connected.
SQL> create table site (col1 varchar2(15))
  2  /
 
Table created.

SQL>

JLS created a procedure to update SOMEBODY's SITE table using its fully qualified name:
 
SQL> conn jls/jls
Connected.
SQL> create or replace procedure update_site as
  2  begin
  3  update somebody.site set col1 = '10 High Street';
  4  end;
  5  /
 
Procedure created.

SQL>

DBA_DEPENDENCIES showed that JLS's UPDATE_SITE procedure referenced SOMEBODY's SITE table:
 
SQL> select referenced_owner, referenced_name
  2  from dba_dependencies
  3  where owner = 'JLS'
  4  and name = 'UPDATE_SITE'
  5  /
 
REFERENCED_OWNER     REFERENCED_NAME
-------------------- ------------------------------
SYS                  SYS_STUB_FOR_PURITY_ANALYSIS
SOMEBODY             SITE

SQL>

CBS created its own SITE table but this did not affect the validity of JLS's UPDATE_SITE procedure:
SQL> conn cbs/cbs
Connected.
SQL> create table site (col2 number)
  2  /
 
Table created.
 
SQL> select owner, object_name, object_type
  2  from dba_objects
  3  where status = 'INVALID'
  4  /
 
no rows selected
 
SQL> drop table site
  2  /
 
Table dropped.

SQL>

A public synonym called SITE was created for SOMEBODY's SITE table:
 
SQL> create or replace public synonym site for somebody.site
  2  /
 
Synonym created.

SQL>

CBS recreated its SITE table. JLS's UPDATE_SITE procedure remained VALID:
 
SQL> create table site (col2 number)
  2  /
 
Table created.
 
SQL> select owner, object_name, object_type
  2  from dba_objects
  3  where status = 'INVALID'
  4  /
 
no rows selected
 
SQL> drop table site
  2  /
 
Table dropped.

SQL>

JLS changed its UPDATE_SITE procedure to access SOMEBODY's SITE table via the public synonym:
 
SQL> conn jls/jls
Connected.
SQL> create or replace procedure update_site as
  2  begin
  3  update site set col1 = '10 High Street';
  4  end;
  5  /
 
Procedure created.

SQL>

DBA_DEPENDENCIES showed that JLS's UPDATE_SITE procedure referenced a public synonym called SITE:
 
SQL> select referenced_owner, referenced_name
  2  from dba_dependencies
  3  where owner = 'JLS'
  4  and name = 'UPDATE_SITE'
  5  /
 
REFERENCED_OWNER     REFERENCED_NAME
-------------------- ------------------------------
SYS                  SYS_STUB_FOR_PURITY_ANALYSIS
PUBLIC               SITE

SQL>

CBS checked that there were no INVALID objects in the database and recreated its SITE table. This made JLS's UPDATE_SITE procedure INVALID.
    
SQL> conn cbs/cbs
Connected.
SQL> select owner, object_name, object_type
  2  from dba_objects
  3  where status = 'INVALID'
  4  /
 
no rows selected
 
SQL> create table site (col2 number)
  2  /
 
Table created.
 
SQL> select owner, object_name, object_type
  2  from dba_objects
  3  where status = 'INVALID'
  4  /
 
OWNER      OBJECT_NAME     OBJECT_TYPE
---------- --------------- ---------------
JLS        UPDATE_SITE     PROCEDURE

SQL>

The procedure then had to be recompiled: 

SQL> conn jls/jls
Connected.
SQL> alter procedure update_site compile
  2  /
 
Procedure altered.
 
SQL>

So, there you have it. If a compiled procedure or package accesses a table via a public synonym, then another user creates a table with the same name as the public synonym, the compiled procedure or package will be invalidated.

Incidentally, when this happened for real, a package with over 3000 lines of code became INVALID. When a UNIX script then tried to run it, Oracle did not attempt to recompile the package automatically, it just failed with a tnsnames related error, possibly because the package accessed data over a database link. It was later recompiled without errors and the UNIX script ran successfully.

I have already looked at a very simple example relating to automatic procedure recompilation. I will be looking at it in more detail in the near future as it is clearly not as straightforward as I thought at first.

Thursday, December 13, 2012

The Curious Case of the Missing PL/SQL Procedure

I have to run files of SQL on databases for developers almost every day. These files often create or recreate PL/SQL packages. Any errors usually go back to the developers for correction as I am not a PL/SQL expert. Yesterday I ran some of these files then a tester asked me why a screen had stopped working. The screen was calling a PL/SQL package which I had just recreated for the developer. I will call it package_b. It had compilation errors and these were causing the screen to fail. The developer was on leave so I decided to have a look at the problem. I listed the compilation errors and could see that they happened when package_b tried to call a new procedure in another package. I will call the other package package_a and I will call the new procedure procedure_a2. I looked for compilation errors in package_a but there were none. I looked at the description for package_a but procedure_a2 was not mentioned. This was because the developer had provided a new body for package_a but no new header. The new header was found and package_a was recompiled. It was then possible to recompile package_b and the tester’s screen started to work again. I have reproduced this problem below on Oracle 11.2.0.2.7:
 
I created package_a and showed that its description contained procedure_a1:
 
SQL> create or replace package package_a is
  2  procedure procedure_a1;
  3  end package_a;
  4  /
 
Package created.
 
SQL> create or replace package body package_a is
  2  procedure procedure_a1 is
  3  begin
  4  null;
  5  end procedure_a1;
  6  end package_a;
  7  /
 
Package body created.
 
SQL> desc package_a
PROCEDURE PROCEDURE_A1
 
SQL>
 
I created package_b containing procedure_b1. This procedure called package_a.procedure_a1. Then I ran package_b.procedure_b1 successfully:
 
SQL> create or replace package package_b is
  2  procedure procedure_b1;
  3  end package_b;
  4  /
 
Package created.
 
SQL> create or replace package body package_b is
  2  procedure procedure_b1 is
  3  begin
  4  package_a.procedure_a1;
  5  end procedure_b1;
  6  end package_b;
  7  /
 
Package body created.
 
SQL> exec package_b.procedure_b1;
 
PL/SQL procedure successfully completed.
 
SQL>
 
I added procedure_a2 to package_a but did not change its header. Then I described it but could only see procedure_a1:
 
SQL> create or replace package body package_a is
  2  procedure procedure_a1 is
  3  begin
  4  null;
  5  end procedure_a1;
  6  procedure procedure_a2 is
  7  begin
  8  null;
  9  end procedure_a2;
10  end package_a;
11  /
 
Package body created.
 
SQL> desc package_a
PROCEDURE PROCEDURE_A1
 
SQL>
 
Then I changed package_b to call package_a.procedure_a2 but it failed to compile:
 
SQL> create or replace package body package_b is
  2  procedure procedure_b1 is
  3  begin
  4  package_a.procedure_a1;
  5  package_a.procedure_a2;
  6  end procedure_b1;
  7  end package_b;
  8  /
 
Warning: Package Body created with compilation errors.
 
SQL> show errors
Errors for PACKAGE BODY PACKAGE_B:
 
LINE/COL
-------------------------------------------------------
ERROR
-------------------------------------------------------
5/1
PL/SQL: Statement ignored
 
5/11
PLS-00302: component 'PROCEDURE_A2' must be declared
 
SQL>
 
I changed the header for package_a. Then its description included procedure_a2:
 
SQL> create or replace package package_a is
  2  procedure procedure_a1;
  3  procedure procedure_a2;
  4  end package_a;
  5  /
 
Package created.
 
SQL> desc package_a
PROCEDURE PROCEDURE_A1
PROCEDURE PROCEDURE_A2
 
SQL>
 
This allowed me to compile package_b and run package_b.procedure_b1 again:
 
SQL> alter package package_b compile
  2  /
 
Package altered.
 
SQL> exec package_b.procedure_b1;
 
PL/SQL procedure successfully completed.
 
SQL>