Watch out with function result cache based on data dictionary views

Result cache is a powerful tool to gain performance in PL/SQL.
There are many examples on the internet that proves this, e.g. these articles on All things Oracle:
Result Cache(1)
Result Cache(2)

But I’m not going to talk about performance.
This article is some kind of warning.

First I’ll show you how result cache works on a normal view.
I’ll create a table, a view on this table and a function that counts the rows in the view.

SQL> create table x (field1 varchar2(1), field2 number(1));

Table created.

SQL> create or replace view vie_x as select * from x;

View created.

SQL> CREATE OR REPLACE FUNCTION vie_x_rowcount(p_field1 IN VARCHAR2)
RETURN NUMBER RESULT_CACHE
IS
   l_return NUMBER;
BEGIN
   SELECT count(*)
     INTO l_return
     FROM vie_x
     WHERE field1 = p_field1;

   RETURN l_return;

END vie_x_rowcount;
/

Function created.

SQL> insert into x(field1, field2) values('x', 1);

1 row created.

SQL> commit;

Commit complete.

These are the statistics for the result cache, just to show you we’re starting without any caching.

SQL> select name, value from v$result_cache_statistics where name in ('Create Count Success', 'Find Count', 'Invalidation Count');

NAME			               VALUE
------------------------------ ------------------------------
Create Count Success	       0
Find Count		               0
Invalidation Count	           0

When we execute the function, the statistics show that there’s an entry created in the cache.

SQL> select vie_x_rowcount('x') from dual;

VIE_X_ROWCOUNT('X')
-------------------
		  1

SQL> select name, value from v$result_cache_statistics where name in ('Create Count Success', 'Find Count', 'Invalidation Count');

NAME			               VALUE
------------------------------ ------------------------------
Create Count Success	       1
Find Count		               0
Invalidation Count	           0

When we execute the same code again, we’ll get the same result and the statistics show us that the result is found in the cache.
Good job Oracle!

SQL> select vie_x_rowcount('x') from dual;

VIE_X_ROWCOUNT('X')
-------------------
		  1

SQL> select name, value from v$result_cache_statistics where name in ('Create Count Success', 'Find Count', 'Invalidation Count');

NAME			               VALUE
------------------------------ ------------------------------
Create Count Success	       1
Find Count		               1
Invalidation Count	           0

Let’s insert a new row in the table.
This time the statistics show us that the cache is “invalidated”, meaning the function has to be executed again to return the correct value.

SQL> insert into x values('x', 2);

1 row created.

SQL> commit;

Commit complete.

SQL> select name, value from v$result_cache_statistics where name in ('Create Count Success', 'Find Count', 'Invalidation Count');

NAME			               VALUE
------------------------------ ------------------------------
Create Count Success	       1
Find Count		               1
Invalidation Count	           1

And the expected result…

SQL> select vie_x_rowcount('x') from dual;

VIE_X_ROWCOUNT('X')
-------------------
		  2

The Oracle database has its own data dictionary, a set of tables where it stores all information about the database and what’s in it.
Data of these tables are available through views, data dictionary views.
In the following example I’ll use the data dictionary view that holds the information on columns.
I created a function that returns the number of columns for a certain table.

SQL> CREATE OR REPLACE FUNCTION number_of_columns(p_table_name VARCHAR2)
RETURN NUMBER RESULT_CACHE
IS

   l_return NUMBER;

BEGIN

   SELECT count(*)
     INTO l_return
     FROM user_tab_columns
    WHERE table_name = p_table_name;

   RETURN l_return;

END number_of_columns;
/

Function created.

To make sure we’ll start with a clean cache, I’ll flush it using the dbms_result_cache.flush procedure.

SQL> execute dbms_result_cache.flush

PL/SQL procedure successfully completed.

SQL> select name, value from v$result_cache_statistics where name in ('Create Count Success', 'Find Count', 'Invalidation Count');

NAME			               VALUE
------------------------------ ------------------------------
Create Count Success	       0
Find Count		               0
Invalidation Count	           0

When we execute the function, we’ll get the expected result: the function is executed and a cache entry is created.

SQL> select number_of_columns('X') from dual;

NUMBER_OF_COLUMNS('X')
----------------------
		     2

SQL> select name, value from v$result_cache_statistics where name in ('Create Count Success', 'Find Count', 'Invalidation Count');

NAME			               VALUE
------------------------------ ------------------------------
Create Count Success	       1
Find Count		               0
Invalidation Count	           0

We can execute it again and see that the return value is retrieved from the cache.

SQL> select number_of_columns('X') from dual;

NUMBER_OF_COLUMNS('X')
----------------------
		     2

SQL> select name, value from v$result_cache_statistics where name in ('Create Count Success', 'Find Count', 'Invalidation Count');

NAME			               VALUE
------------------------------ ------------------------------
Create Count Success	       1
Find Count		               1
Invalidation Count	           0

Let’s add a column to the table.
This should add a new row in a data dictionary table and thus in the data dictionary view we use in our function.

SQL> alter table x add (field3 date);

Table altered.

SQL> desc x
 Name					   Null?    Type
 ------------------------- -------- ----------------------------
 FIELD1 					        VARCHAR2(1)
 FIELD2 					        NUMBER(1)
 FIELD3 					        DATE

Now execute the function again.
And the result is…

SQL> select number_of_columns('X') from dual;

NUMBER_OF_COLUMNS('X')
----------------------
		     2

Not what we expected!
When we take a look at the result cache statistics, it shows that the cache wasn’t invalidated and the result was retrieved from the result cache.

SQL> select name, value from v$result_cache_statistics where name in ('Create Count Success', 'Find Count', 'Invalidation Count');

NAME			               VALUE
------------------------------ ------------------------------
Create Count Success	       1
Find Count		               2
Invalidation Count	           0

When we flush the cash and execute the function again, we’ll get the correct result cache.

SQL> execute dbms_result_cache.flush

PL/SQL procedure successfully completed.

SQL> select number_of_columns('X') from dual;

NUMBER_OF_COLUMNS('X')
----------------------
		     3

So, it seems that the result cache isn’t invalidated on data dictionary tables.
And indeed this is what I found in the Oracle documentation:

You cannot cache results when the following objects or functions are in a query:

  • Temporary tables and tables in the SYS or SYSTEM schemas

Gartner Report: “Modernization and Migration Strategies for Oracle Forms”

Grant Ronald brought this Gartner Report to attention on his blog: “Modernization and Migration Strategies for Oracle Forms“.

It’s great to see that our message brought to our customers and at our presentations is confirmed by this Gartner analysis

  • Upgrade to the latest version – At the moment this is Oracle Forms 11gR2
  • Modernize and integrate
  • Migrate(ADF, APEX, Open Source, …)

If you want more information on this topic, you can read my article on All Things Oracle: “What’s Your Choice for Oracle Forms?“.

Oracle Forms 11gR2 released

In a previous post I wrote that Grant Ronald announced the release of Oracle Forms 11gR2 in our Oracle Open World presentation.  He didn’t announce an exact date, but it would be very soon.

Well, here it is!

The new version can be downloaded from Oracle Technology Network.
The installation footprint is reduced, it can be integrated in Oracle Access Manager and some other new features.

 

 

* Update

Client support:

  • Browser support is no longer based on Operating Systems but strictly tied to the browser themselves, no matter which Operating Systems they are installed on.
  • Oracle Forms is supported on both 32 bit browsers with 32 bit Oracle JRE & 64 bit browsers with 64 bit Oracle JRE combinations.

System support:

  • Oracle Weblogic Server: WLS 10.3.5
  • Databases: Oracle 10.2.0.4+, Oracle 11.1.0.7+, Oracle 11.2.0.1+
  • 32 bit and 64 bit systems

More about certifications.

Oracle Forms 11g and Google maps integration

In last years blog post about Forms 11g Javascript integration I explained how you could call the Google Maps API from forms using JavaScript.

I captured that testcase using screentoaster.
It seems that this tool has disappeared and all the recorded sessions with it.

So, here’s a new video on youtube of this Google Map Integration, now in a demo application.
The Form is embedded in a HTML page witch contains a Google Map.

OBUG Connect, the Oracle Benelux Usergroup conference in Brussels.

Opening ceremony by Wim Coekaerts & Janny Ekelson.
Nothing much to say about this…

First keynote session was brought by Chris Leone about Oracle Fusion applications.
Applications is not my thing, but it was nice to see how everything in Fusion apps is integrated like BI and collaboration.

My first session was “The best way” by Tom Kyte, a session about doing things the “best way” or “best practices”.
Tom quoted Bryn Llewellyn on what brings you to best practices.
It depends on things from “reasoning skills” over “education” to “know oracle inside out” to “know pl/sql inside out”.

An example join two big tables(big… big tables) with little distinct values.
What will be the fastest(best) way to retrieve records for one of those distinct values: hash joins or index scans?
In a batch operation the hash joins will be the fastest, but on a screen that only shows 20 records?

So, when is something the best way?  Well, it depends…

How can you tune using TKPROF?
A best practice…
Get the facts(physical I/O, logical I/O, difference between CPU and elapsed time,…).
Infer more facts.  Know your data, know how oracle works.
Build your context.
Rule things out.
Very interesting session!

Time for lunch!

Next session was one of Lucas Jellema and Patrick Stevens: “Randstad’s modernization of organization, architecture and applications powered by Fusion Middleware”.
They explained how they transformed the IT team to work with the agile approach.
This resulted in a faster develoment(about 4 times) and a team that is more involved.
Randstad also decided to make their applications service based.
So a service layer was build around all core processes using BPEL and OSB.
The only problem is Forms, which still accesses the database directly.
The Forms application will fade away in the future to a web application in ADF…

Last session was another AMIS session by Luc Bors together with Simon Vos of bol.com: “How BOL.COM benefited from ADF”.
Bol.com decided in 2007 to move to ADF.
Some reasons to move:
– Oracle statement of direction:  exit designer
– no authorization/authentication
– forms supported datamodel, not business processes
Where did they want to go to:
– SSO
– new and extended UI
– add reporting
– no direct database access

So they introduced scrum, ADF and trained they’re inhouse (forms)developers to use JDeveloper, ADF and JHeadstart.
Now they could start to rebuild the forms application in ADF.
The pl/sql and built-ins used in forms are put in the database or, if lucky, they could use an ADF alternative.
Others(little percentage) had to be programmed in Java.

This resulted in a new application with the same functionality(allthough some additional functionality was added) as the forms application with a new look and feel.

Some interesting sessions, allthough I like to see some more demos next time.

Upgrading to Forms 11g

Grant Ronald has just published three references for Oracle Forms upgrades to 11g.
Those references can be found on his blog.

As Grant writes, it’s pretty easy to upgrade, just recompile and deploy.

A big advantage of migrating to Forms 11g is that it opens up the integration of the forms application with other technologies(eg. ADF).

Upgrade your Forms application and enter the age of fusion!

Update on “Forms 11G and DB function with result cache”

In my previous post I mentioned the problem with compiling a program unit that calls a database function with result cache.

Like I wrote in that post, the problem exists in the PL/SQL Client version.
There’s a patch available to update the PL/SQL client: Oracle Database Server Version 11.1.0.7 Patch 33.

This is a database patch, but it can also be applied to the middleware home.

After applying the patch, the program unit compiles…Problem solved!

So, go and start using Result cache!

Forms 11G and DB function with result cache

Result cache… a very cool feature in the 11g database.
If you don’t know it, this is a must read: Pl/SQL function result cache in 11g

But also watch out with it in Forms 11g.
It seems there’s a bug.
When compiling a program unit that calls a function with result cache in forms, you’ll get a compile error, which look likes this:

Error 801 at line 7, column 2: internal error[*** ASSERT at file pdw1.c, line 4061; PSDGON missing. Can’t get object number; XNSPC1PTEST__P[7,2]

This is logged on “My Oracle Support” as a bug.
It’s not a forms bug, but a PL/SQL Client bug.
And this “bad” version of the PL/SQL client is used in forms11g(or in general FMW11g).
It works with older and newer versions of the PL/SQL Client(Forms 10g + function result cache should work).
Hopefully there’s a patch soon, so that everybody can use result cache ;-)