Thursday 12 December 2013

Yet another one on 'Oracle Type inheritance and advanced type casting'

Triggered by a comment on an older article I wrote a little article yesterday on determining the instance of an object. As far as known by me at least and seemingly by Google there is no java instanceOf counterpart in Oracle Pl/Sql. In SQL Where clauses you can use the 'is of' expression. But that is not available in pl/sql. I tried some suggestions with embedded sql but I couldn't get it to work. The suggestion from a forum I found yesterday was to use function overloading. I played around a little with it and it seems I stumbled on a fairly simple method. Let's say I've a type like the following:
CREATE OR REPLACE TYPE "DWN_PERSON" AS OBJECT 
( 
name varchar2(40)
, member function instance_of return varchar2
) not final;
/
CREATE OR REPLACE TYPE BODY "DWN_PERSON" AS
  member function instance_of return varchar2 AS
  BEGIN
    return 'DWN_PERSON';
  END instance_of;
END;
/
I just created a simple Instance_of method that does nothing but returning the name of the object. It was my first step in making things spiffier later on. Keeping in mind that it probably should become an overloading function with a parameter. Then I created a Natural Person type:
  CREATE OR REPLACE TYPE "DWN_NATURAL_PERSON" under DWN_PERSON 
( surname varchar2(100)
, overriding member  function instance_Of return varchar2 
) ;
/
CREATE OR REPLACE TYPE BODY "DWN_NATURAL_PERSON" AS
  overriding member  function instance_Of return varchar2 AS
  BEGIN
    RETURN 'DWN_NATURAL_PERSON';
  END instance_Of;
END;
/
And because I was going so well, I created a Not Natural Person:
CREATE OR REPLACE TYPE "DWN_NOT_NATURAL_PERSON" under DWN_PERSON 
( companyname varchar2(100)
, overriding member  function instance_Of return varchar2 
) ;
/
CREATE OR REPLACE TYPE BODY "DWN_NOT_NATURAL_PERSON" AS
  overriding member  function instance_Of return varchar2 AS
  BEGIN
    RETURN 'DWN_NOT_NATURAL_PERSON';
  END instance_Of;
END;
/
Then I created the following little script:
declare
l_person1 dwn_person;
l_person2 dwn_person;
l_person3 dwn_person;

function create_person(p_name varchar2, p_surname varchar2, p_comany_name varchar2) return dwn_person
as
l_person dwn_person;
begin
  if p_surname is not null then
  l_person := dwn_natural_person(p_name, p_surname);
  elsif p_comany_name is not null then
    l_person := dwn_not_natural_person(p_name, p_comany_name);
  else
  l_person := dwn_person(p_name);
  end if;
 return l_person;
end;

begin
  l_person1 := create_person('Flip', 'Fluitketel', null);
  dbms_output.put_line('l_person1 is a '||l_person1.instance_of);
  l_person2 := create_person('Hatseflats', null, null);
  dbms_output.put_line('l_person2 is a '||l_person2.instance_of);
  l_person3 := create_person('Hatseflats', null, 'Hatseflats Inc.');
  dbms_output.put_line('l_person3 is a '||l_person3.instance_of);
end;
All three persons are declared as a 'DWN_PERSON'. So when I call the instance_of method of the particular object I expected that the method of the particular declared type is called. But, a little to my surprise, it turns out that Oracle uses the method of the Instantiated type:
l_person1 is a DWN_NATURAL_PERSON
l_person2 is a DWN_PERSON
l_person3 is a DWN_NOT_NATURAL_PERSON
Simple, but apparently very effective.

Wednesday 11 December 2013

Another one on "Oracle Type inheritance and advanced type casting"

Already four and a half year ago I wrote an article on Oracle Type inheritance and type casting.  See here. But although Object types are supported by Oracle since Oracle 8, it seems to me that there is still little familiarity on the subject.
I'm not really a Pl/Sql programmer on daily basis anymore, the language is still close to me. And working with object types is a favorite subject to me ever since I discovered the endless use in Oracle 8i. Know Object types and you can do everything in Oracle.

Today I got a comment on the article, asking on if there is a solution on "downcasting" types. The subject in the mentioned article was that from an API (in the example from an EBusiness Suite12  API) you get an object type to which you want to add functionality without re-implementing/changing the delivered object type. The only thing to do it is to sub-type the delivered type. But how to come from an instance of a supertype to the use it as the subtype. Well, the answer is two fold:
  1. If the instance of the super type is actually instantiated as the supertype then you actually can't. As explained in the mentioned article, the only way is to instantiate the subtype with the attributes of the supertype. For what I think I found a smart generic solution.
  2. If you get an instance of the supertype, that was actually instantiated as the subtype then you can copy it again to a variable based on the subtypehttp://blog.darwin-it.nl/ and your good to go.
Lets elaborate on the second option. I'll do a dry swim, so the code presented is not tested in a database, so might not be free from syntax errors.

In Java there is a  statement called "InstanceOf". In Oracle SQL you have the "Is Of"construct. I found a forum post with the question about an "InstanceOf" analogy in Pl/Sql. As in the forum is stated the "Is Of" clause is in fact a possible where clause condition in a query. See also the documentation.

However in the post I saw a smart comment: create an overloaded procedure:
create procedure p_x(p_obj super_type);
do supertype code here

create procedure p_x(p_obj sub_type);
do subtype code here
Now let's say we have a type named 't_car' and a subtype say 't_opel_zafira under t_car':
create type t_car as object
(license_plate varchar2(10) )
/

create type t_opel_zafira under t_car 
(model varchar2(10))
/
Then you could create an overloaded function like
create function instance_of(p_obj t_car ) returns varchar2
as
begin
  return 't_car';
end;

create function instance_of(p_obj t_opel_zafira ) returns varchar2
as
begin
  return 't_opel_zafira';
end;
Then you could indeed do something like:
declare
  l_car t_car;
  l_opel_zafira t_opel_zafira;
begin
  l_car := instance_opel_zafira_as_car(...); -- This functions returns a value of type t_car but is in fact an Opel Zafira
  if instance_of(l_car) = 't_opel_zafira' then
    dbms_output.put_line('Enjoy your Opel Zafira');
  end if;
end;
I should try it, but you should even be able to add a such an instance_of member function to each object type. Going to try that tomorrow.