How to call an Oracle PL/SQL object super method - oracle

I'd like to call an overridden PL/SQL method. Here's an example:
-- super class
create or replace type test as object
(
n number,
member procedure proc(SELF in out nocopy test, s varchar2)
)
alter type test not final
/
create or replace type body test is
member procedure proc(SELF in out nocopy test, s varchar2) is
begin
dbms_output.put_line('test1: n='||nvl(self.n, 'null')||' s='||s);
self.n := to_number(s);
end;
end;
/
-- derived class
create or replace type test2 under test
(
overriding member procedure proc(SELF in out nocopy test2, s varchar2)
)
/
Now I want to invoke the inherited version of the proc method. When I try to do an explicit cast like treat(self as test).proc(s); it won't compile because of PLS-00363: expression 'SYS_TREAT' cannot be used as an assignment target
The type body compiles when I use a local variable:
create or replace type body test2 is
overriding member procedure proc(SELF in out nocopy test2, s varchar2) is
O test;
begin
O := treat(self as test);
O.proc(s);
end;
end;
/
But when I run my example like this
declare
obj test2;
begin
obj := test2(0);
obj.proc('1');
end;
...it throws ORA-21780: Maximum number of object durations exceeded.
Is there any way to call test::proc (without serializing/deserializing)?
And... after proc has been called, how can any changed attributes (namely n) be reflected in obj ?
Update (Thanks, tbone):
I changed the organization of my methods using template methods ('before' and 'after'). I add them whenever I need to extend a method.
create or replace type test as object
(
n number,
member procedure proc (SELF in out nocopy test, s varchar2),
member procedure afterProc (SELF in out nocopy test, s varchar2)
member procedure beforeProc(SELF in out nocopy test, s varchar2),
)
not final
/
create or replace type body test is
member procedure proc(SELF in out nocopy test, s varchar2) is
begin
beforeProc(s);
dbms_output.put_line('test1: n='||nvl(n, 'null')||' s='||s);
n := to_number(s);
afterProc(s);
end;
member procedure afterProc (SELF in out nocopy test, s varchar2) is begin null; end;
member procedure beforeProc(SELF in out nocopy test, s varchar2) is begin null; end;
end;
/

To access the super methods, try either general invocation or generalized expression. For example, using a person supertype and student subtype:
CREATE OR REPLACE TYPE person_typ AS OBJECT (
idno number,
name varchar2(30),
phone varchar2(20),
MAP MEMBER FUNCTION get_idno RETURN NUMBER,
MEMBER FUNCTION show RETURN VARCHAR2)
NOT FINAL;
CREATE OR REPLACE TYPE BODY person_typ AS
MAP MEMBER FUNCTION get_idno RETURN NUMBER IS
BEGIN
RETURN idno;
END;
MEMBER FUNCTION show RETURN VARCHAR2 IS
BEGIN
-- function that can be overriden by subtypes MEMBER FUNCTION show RETURN VARCHAR2 IS BEGIN
RETURN 'Id: ' || TO_CHAR(idno) || ', Name: ' || name;
END;
END;
CREATE TYPE student_typ UNDER person_typ (
dept_id NUMBER,
major VARCHAR2(30),
OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2)
NOT FINAL;
CREATE TYPE BODY student_typ AS
OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2 IS
BEGIN
RETURN (self AS person_typ).show || ' -- Major: ' || major ;
END;
END;
-- Using Generalized Invocation
DECLARE
myvar student_typ := student_typ(100, 'Sam', '6505556666', 100, 'Math');
name VARCHAR2(100);
BEGIN
name := (myvar AS person_typ).show; --Generalized invocation
END;
-- Using Generalized Expression
DECLARE
myvar2 student_typ := student_typ(101, 'Sam', '6505556666', 100, 'Math');
name2 VARCHAR2(100);
BEGIN
name2 := person_typ.show((myvar2 AS person_typ)); -- Generalized expression
END;
EDIT:
If you are on 10g, you'll need to organize the functions a bit different, but same functionality from the child to call the super method:
CREATE TYPE BODY person_typ AS
MAP MEMBER FUNCTION get_idno RETURN NUMBER IS
BEGIN
RETURN idno;
END;
-- static function that can be called by subtypes
STATIC FUNCTION show_super (person_obj in person_typ) RETURN VARCHAR2 IS
BEGIN
RETURN 'Id: ' || TO_CHAR(person_obj.idno) || ', Name: ' || person_obj.name;
END;
-- function that can be overriden by subtypes
MEMBER FUNCTION show RETURN VARCHAR2 IS
BEGIN
RETURN person_typ.show_super ( SELF );
END;
END;
CREATE TYPE student_typ UNDER person_typ (
dept_id NUMBER,
major VARCHAR2(30),
OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2)
NOT FINAL;
CREATE TYPE BODY student_typ AS
OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2 IS
BEGIN
RETURN person_typ.show_super ( SELF ) || ' -- Major: ' || major ;
END;
END;
Now you'd call show_super() from student for the person method, or just show() for the student method.
From the docs, hope that helps.

Related

what is wrong passing a record type as parameter

I have the following code snippets:
CREATE OR REPLACE PACKAGE pkg_test1 IS
TYPE t_rec1 is RECORD(c1 number);
FUNCTION f1(p1 t_rec1 ) RETURN VARCHAR2;
END pkg_test1;
CREATE OR REPLACE PACKAGE BODY pkg_test1 IS
FUNCTION f1 (p1 t_rec1) return varchar2 is
BEGIN
RETURN 'a';
END f1;
END pkg_test1;
DECLARE
TYPE t_rec IS RECORD (c1 NUMBER);
v_rec t_rec;
v_var VARCHAR2(15);
BEGIN
v_rec.c1 := 1;
v_var := pkg_test1.f1 (v_rec);
END;
When I execute the anonym code, I received the following error:
PLS-00306: wrong number or types of arguments in call to 'F1'
Can someone help me to see the mistake I make, please?
The type you declare in your anonymous block looks the same to you, but to Oracle it's a completely independent and incompatible type.
From the documentation:
A RECORD type defined in a package specification is incompatible with an identically defined local RECORD type.
You don't need it though; just use the package type:
DECLARE
-- don't create a new type....
--TYPE t_rec IS RECORD (c1 NUMBER);
--v_rec t_rec;
-- use the package type instead
v_rec pkg_test1.t_rec1;
v_var VARCHAR2(15);
BEGIN
v_rec.c1 := 1;
v_var := pkg_test1.f1 (v_rec);
END;
db<>fiddle

PLS-00201 – identifier must be declared, passing a collection to procedure

I'm trying to pass a collection to procedure, but when I compile the package, I'll get this message:"PLS-00201 – identifier must be declared".
this is my code:
create or replace package PACK_DW_TEMP
as
procedure A (.......);
--
procedure B (error_list in out l_error);
end PACK_DW_TEMP;
In the package body I've created the collection in the procedure A and passed it to procedure B
create or replace package body PACK_DW_TEMP
as
procedure A ( ........ )
as
begin
declare
type error IS RECORD(
cod_error NUMBER,
descr_error VARCHAR2(100)
);
type l_errori is table of error;
error_list l_error := l_error();
begin
procedure B(error_list);
end;
end;
Into procedure B:
procedure B ( error_list in out l_error )
as
begin
declare
i NUMBER;
type err IS RECORD(
cod_error NUMBER,
descr_error VARCHAR2(100)
);
type l_err is table of err;
err_list l_err := l_err();
begin
i := 0;
for i in 1..5 loop
err_list(i).cod_err := error_list(i).cod_error;
err_list(i).descr_err := error_list(i).descr_error;
end loop;
end;
end;
My target is to pass the collection to procedure B and to assegn values to the new collection.
You're defining a type called l_err in the package body but in procedure B you're trying to use a type called l_error. Also, you have to define l_error in the package spec, prior to its first use:
create or replace package PACK_DW_TEMP
as
type err IS RECORD(
cod_error NUMBER,
descr_error VARCHAR2(100)
);
type l_error is table of err;
procedure A (.......);
--
procedure B (error_list in out l_error);
end PACK_DW_TEMP;
and remove the definitions of type_err, l_errori, and l_err from the package body.
A type declared locally to a procedure and another type declared locally to another procedure with exactly the signature are not the same types and you cannot pass one to the other. You need to create the type externally to the procedures rather than internally.
Also, you cannot use the type in the signature for the package when it is not declared except internally to the procedure which is declared in the body of the package.
create or replace package PACK_DW_TEMP
as
TYPE error IS RECORD(
cod_error NUMBER,
descr_error VARCHAR2(100)
);
type l_error is table of error;
procedure A;
procedure B (error_list in out l_error);
end PACK_DW_TEMP;
/
and
create or replace package body PACK_DW_TEMP
as
procedure A
as
error_list l_error := l_error();
begin
error_list.EXTEND(2);
error_list(1).cod_error := 1;
error_list(1).descr_error := 'DESCR1';
error_list(2).cod_error := 2;
error_list(2).descr_error := 'DESCR2';
B(error_list);
FOR i IN 1 .. error_list.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(
error_list(i).cod_error || ': ' || error_list(i).descr_error
);
END LOOP;
end;
procedure B ( error_list in out l_error )
as
begin
error_list.EXTEND;
error_list(error_list.COUNT).cod_error := 99;
error_list(error_list.COUNT).descr_error := 'DESCR99';
end;
end;
/
Then you can call it using:
BEGIN
PACK_DW_TEMP.A();
END;
/
Which outputs:
1: DESCR1
2: DESCR2
99: DESCR99
db<>fiddle here

Setter methods in UDT in Oracle database

I'm still new to this and trying to get my head around it. UDT are able to define methods which you can call on the object stored, I seem to create a method that returns a value fine but was wondering if it's possible create a setter methods. This scenario isn't really useful but it's simple just for clarification
For example, I have this type:
create TYPE TestType2 AS OBJECT(
Numb NUMBER(4),
Str VARCHAR2(10),
MEMBER FUNCTION setNum(numba NUMBER) RETURN NUMBER
);
Which compiles fine so my assumption setter methods are allow
I've tried create the body type below:
CREATE TYPE BODY TestType2 as
member function setNum(numba NUMBER) return NUMBER is
begin
SELF.Numb := numba;
return SELF.Numb;
END;
END;
However this won't work giving me the errors below:
Error(3,9): PL/SQL: Statement ignored
Error(3,14): PLS-00363: expression 'SELF.NUMB' cannot be used as an assignment target
Is there a way to create a set method or is this only allowed in store procedures?
This is an obscure error. The problem is member functions take an implicit parameter of SELF. So if you want to change something you need to make the parameter explicit:
create or replace TYPE TestType2 AS OBJECT(
Numb NUMBER(4,0),
Str VARCHAR2(10),
MEMBER procedure setNum(self in out TestType2, numba NUMBER )
);
/
CREATE or replace TYPE BODY TestType2 as
member function setNum(self in out TestType2 , numba NUMBER) return NUMBER
is
begin
self.Numb := numba;
return SELF.Numb;
END;
END;
/
Note that the SELF parameter remains implicit when calling the function:
declare
t TestType2 := TestType2(4, 'TEST');
n pls_integer;
begin
dbms_output.put_line('1' || t.numb);
n := t.setNum(8);
dbms_output.put_line('2' || t.numb);
end;
/
Incidentally, setter methods don't need to be functions; we can have member procedures too.
create or replace TYPE TestType2 AS OBJECT(
Numb NUMBER(4,0),
Str VARCHAR2(10),
MEMBER procedure setNum(self in out TestType2, numba NUMBER ),
MEMBER FUNCTION getNum RETURN NUMBER
);
/
CREATE or replace TYPE BODY TestType2 as
member procedure setNum(self in out TestType2, numba NUMBER )
is
begin
self.Numb := numba;
END;
MEMBER FUNCTION getNum RETURN NUMBER
is
begin
return self.numb;
end;
END;
/

CLOB Performance Issue in PLSQL

I've created the below function . When we write SQL queries using this function, the query returning 10K records is running for a very long time 2 hours. I figured out the performance Issue is due to CLOB usage in the function. When I change the CLOB reference in the below type and function to Varchar2 the query runs in a sec.
But, I don't want to change the data type to Varchar2. I wanted to have it as CLOB. But how can I improve the performance?
I do understand that I can go with LISTAGG or other oracle provided string functions. But I wanted the data type as CLOB and wanted to know, why there is such a huge difference in execution.
Below is the code of the User Defined Function. The query sample is given below.
select my_concat(col1),my_concat(col2),my_concat(col3),my_concat(col4) from table group by col1,col2,col3,col4;
CREATE OR REPLACE TYPE "my_concat"
AUTHID CURRENT_USER
AS OBJECT (
curr_str clob,
STATIC FUNCTION odciaggregateinitialize (sctx IN OUT my_concat)
RETURN NUMBER,
MEMBER FUNCTION odciaggregateiterate (
SELF IN OUT my_concat,
p1 IN varchar2
)
RETURN NUMBER,
MEMBER FUNCTION odciaggregateterminate (
SELF IN my_concat,
returnvalue OUT clob,
flags IN NUMBER
)
RETURN NUMBER,
MEMBER FUNCTION odciaggregatemerge (
SELF IN OUT my_concat,
sctx2 IN my_concat
)
RETURN NUMBER
);
/
CREATE OR REPLACE TYPE BODY "my_concat"
IS
STATIC FUNCTION odciaggregateinitialize (sctx IN OUT my_concat)
RETURN NUMBER
IS
BEGIN
sctx := my_concat (NULL);
RETURN odciconst.success;
END;
MEMBER FUNCTION odciaggregateiterate (
SELF IN OUT my_concat,
p1 IN varchar2
)
RETURN NUMBER
IS
BEGIN
IF (curr_str IS NOT NULL)
THEN
curr_str := curr_str || ',' || p1;
ELSE
curr_str := p1;
END IF;
RETURN odciconst.success;
END;
MEMBER FUNCTION odciaggregateterminate (
SELF IN my_concat,
returnvalue OUT clob,
flags IN NUMBER
)
RETURN NUMBER
IS
BEGIN
returnvalue := curr_str;
RETURN odciconst.success;
END;
MEMBER FUNCTION odciaggregatemerge (
SELF IN OUT my_concat,
sctx2 IN my_concat
)
RETURN NUMBER
IS
BEGIN
IF (sctx2.curr_str IS NOT NULL)
THEN
SELF.curr_str := SELF.curr_str || ',' || sctx2.curr_str;
END IF;
RETURN odciconst.success;
END;
END;
/
CREATE OR REPLACE FUNCTION my_func_cat (p1 varchar2)
RETURN clob
AGGREGATE USING my_concat;
/

use User-Defined Aggregate Functions in package

my oracle version is 10.2.
I have a user_defined aggregate functions which using a object like this:
create type strcat_type as object (
cat_string varchar2(4000),
static function ODCIAggregateInitialize(cs_ctx In Out strcat_type) return number,
member function ODCIAggregateIterate(self In Out strcat_type,value in varchar2) return
number,
member function ODCIAggregateMerge(self In Out strcat_type,ctx2 In Out strcat_type)
return number,
member function ODCIAggregateTerminate(self In Out strcat_type,returnValue Out
varchar2,flags in number) return number
)
but when I try to put this object in a package,
create or replace package common is
type strcat_type as object (
cat_string varchar2(4000),
static function ODCIAggregateInitialize(cs_ctx In Out strcat_type) return number,
member function ODCIAggregateIterate(self In Out strcat_type,value in varchar2) return
number,
member function ODCIAggregateMerge(self In Out strcat_type,ctx2 In Out strcat_type)
return number,
member function ODCIAggregateTerminate(self In Out strcat_type,returnValue Out
varchar2,flags in number) return number
)
end common;
It causes pls-00540,pls-00707,how can I create a package with using this object?And how to put my type body and all those static and member functions in package body?
my type body like this:
create type body strcat_type is
static function ODCIAggregateInitialize(cs_ctx IN OUT strcat_type) return number
is
begin
cs_ctx := strcat_type( null );
return ODCIConst.Success;
end;
member function ODCIAggregateIterate(self IN OUT strcat_type,
value IN varchar2 )
return number
is
begin
self.cat_string := self.cat_string || ','|| value;
return ODCIConst.Success;
end;
member function ODCIAggregateTerminate(self IN Out strcat_type,
returnValue OUT varchar2,
flags IN number)
return number
is
begin
returnValue := ltrim(rtrim(self.cat_string,','),',');
return ODCIConst.Success;
end;
member function ODCIAggregateMerge(self IN OUT strcat_type,
ctx2 IN Out strcat_type)
return number
is
begin
self.cat_string := self.cat_string || ',' || ctx2.cat_string;
return ODCIConst.Success;
end;
end;
my function is like this:
CREATE or replace
FUNCTION strcat(input varchar2 )
RETURN varchar2
PARALLEL_ENABLE AGGREGATE USING strcat_type;
how to pack all these into a package and when I need aggregate,I just put 'common.strcat(colName)'?
OBJECT types are SQL objects (like tables, index...), they can't be defined in a package (just like you wouldn't define an index or a view inside a package).
See related SO question: Possible to create Oracle Database object types inside of PL/SQL?

Resources