A collection of tutorials, code and tools to help you get better using Oracle
24 April 2012
Sometime back, I wrote about the new 11gR2 feature that allows PLSQL to be changed without an outage. In that series of articles I pointed to 'fine grained dependency checking' that was introduced in 11gR1 as an enabler for this feature citing that online changes in 10g were impossible. However, there was a subtle, but now pretty obvious point that I missed back then, and some online changes are in fact possible in 10g
In Oracle 10g and before, if a package referenced a table, and the table was modified (by adding a column for example), the package would immediately be marked as invalid. In 11g this invalidation can be avoided, but even if the package does become invalid, it is not as bad as it seems.
The next time the package is called, execution will be delayed for a short period while Oracle recompiles it, and if the compile is successful, it will be called as normal. Even if a connected session has a prepared call to that procedure open, it will handle this sort of invalidation seamlessly. For example:
create table t1 (c1 integer); create or replace package pkg1 as procedure p1; end; / create or replace package body pkg1 as procedure p1 is begin for row in (select * from t1) loop null; end loop; end; end; /
Next I create a simple JDBC program using JRuby. This program doesn't do anything useful, but it illustrates the point. I first prepare and execute a call to my package, then I sleep. While the program is sleeping I add a column to T1 which will invalidate the package (because there is a select * in the procedure P1).
require 'java' java_import 'oracle.jdbc.OracleDriver' java_import 'java.sql.DriverManager' oradriver = OracleDriver.new DriverManager.registerDriver oradriver @connection = DriverManager.get_connection "jdbc:oracle:thin:@localhost:1521/local11gr2.world", 'sodonnel', 'sodonnel' @connection.auto_commit = false call = @connection.prepare_call("begin pkg1.p1; end;") call.execute puts "called once" sleep(15) call.execute puts "called twice"
While the JRuby program is sleeping, I executed the following code:
alter table t1 add (c2 integer); select object_name, status from user_objects where object_name like 'PKG1'; OBJECT_NAME STATUS ------------------- ------- PKG1 VALID PKG1 INVALID
So the change to the table invalidated my package, but the JRuby program completed the second call to the package without an error, proving it is not an issue.
In the above example, the invalidation of the package code is not a problem, as any application will recover seamlessly from it. However, a simple change to the package body can spoil the party:
create or replace package body pkg1 as g_var integer; -- <<- Global variable procedure p1 is begin for row in (select * from t1) loop null; end loop; end; end; /
By adding a global variable to the package, it now has internal state, and this is a problem. Even if this variable is a constant it will still cause problems on invalidations.
If you run the above example again, modifying the table while the JRuby code is sleeping, the second call will produce the following exception:
NativeException: java.sql.SQLException: ORA-04068: existing state of packages has been discarded
This is an artefact of how Oracle maintains the internal package state. A package with no global variable has no internal state, and when it is recompiled Oracle knows it does not have to throw an ORA-04068 if the package is accessed again in the same session.
However, a package with internal state (caused by having a package global variable) will lose that state when it is recompiled, causing the ORA-04068.
To avoid this error, all you have to do is avoid using global variables in your package. That can be easier said than done in some cases, and many well written applications will define a series of constants in the package unknowingly causing this problem.
A workaround is to define a 'constants package' that does nothing other that define the constants, and a separate package that accesses the tables and retrieves the constants from the constants package. In that way, the package with the internal state will not be invalidated by any changes to the underlying schema and active sessions will not see any errors.
In conclusion if you need to modify tables while the application is online, especially in 10g, give careful thought to how the code is organised to avoid unexpected ORA-04068 errors in the application.
Update - if you use stand alone functions or procedures in your application, you may want to read about how you can still get an ORA-04068, which was something I wasn't expecting.