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?
Related
I'm working on a project that requires me to create many different subtypes under one supertype. This supertype is supposed to have a function that gets a name, but the name will only come from the subtype. Is there a way to create the type and allow it to have a function that returns a value from a subtype?
I have tried just removing the function altogether, but in order to complete this project, I need it to have this function, because the function is used through this type to get the information from the objects that I insert into a table.
CREATE OR REPLACE
TYPE test_obj IS OBJECT
( OBJ_ID NUMBER
, OBJ_NAME VARCHAR2(30)
, CONSTRUCTOR FUNCTION test_obj
( OBJ_ID NUMBER
, OBJ_NAME VARCHAR2)
RETURN SELF AS RESULT
, MEMBER FUNCTION GET_OBJ_NAME RETURN VARCHAR2
, MEMBER PROCEDURE SET_OBJ_NAME
( ONAME VARCHAR2)
, MEMBER FUNCTION GET_NAME RETURN VARCHAR2
, MEMBER FUNCTION TO_STRING RETURN VARCHAR2)
INSTANTIABLE NOT FINAL;
/
CREATE OR REPLACE
TYPE BODY test_obj IS
CONSTRUCTOR FUNCTION test_obj
( OBJ_ID NUMBER
, OBJ_NAME VARCHAR2 )
RETURN SELF AS RESULT IS
BEGIN
self.OBJ_ID := OBJ_ID;
self.OBJ_NAME := OBJ_NAME;
RETURN;
END test_obj;
MEMBER FUNCTION GET_OBJ_NAME
RETURN VARCHAR2 IS
BEGIN
RETURN OBJ_NAME;
END GET_OBJ_NAME;
MEMBER PROCEDURE SET_OBJ_NAME
(OBJ_NAME VARCHAR2) IS
BEGIN
self.OBJ_NAME := OBJ_NAME;
END SET_OBJ_NAME;
-- This is the function that I need to fix
MEMBER FUNCTION GET_NAME
RETURN VARCHAR2 IS
NAME VARCHAR2(30)
BEGIN
RETURN NAME;
END GET_NAME;
MEMBER FUNCTION TO_STRING
RETURN VARCHAR2 IS
BEGIN
RETURN '['||self.OBJ_ID||']['||self.OBJ_NAME||']';
END TO_STRING;
END;
/
I want to be able to override the GET_NAME function in subclasses, but I don't want to pass in a NAME variable, because this type won't ever receive a NAME variable, the subtypes are the only ones that will receive it. I won't be able to instantiate it properly if I have the name passed in through the constructor.
Any help would be much appreciated. Thank you.
I have created user-defined types. It has member functions. Almost all use same package function report_pkg.get_data. And I do some other stuff.
create or replace type report_type is object
(
globalvar1 number(15),
globalvar2 varchar2(8),
member function getA return varchar2,
member function getB return varchar2,
member function getC return varchar2,
member function getD return varchar2
)
In member function, I use the same function.
member function getA return varchar2 is
rc tb_example%rowtype;
begin
rc := report_pkg.get_data(globalvar1 , globalvar2);
**Other stuff**
return rc.column1;
end;
member function getB return varchar2 is
rc tb_example%rowtype;
begin
rc := report_pkg.get_data(globalvar1 , globalvar2);
**Other stuff**
return rc.column2;
end;
member function getC return varchar2 is
rc tb_example%rowtype;
begin
rc := report_pkg.get_data(globalvar1 , globalvar2);
**Other stuff**
return rc.column3;
end;
member function getD return varchar2 is
rc tb_example%rowtype;
begin
rc := report_pkg.get_data(globalvar1 , globalvar2);
**Other stuff**
return rc.column4;
end;
When I run the below code in a loop;
report_tab := report_type(globalvar1, globalvar2);
insert into tb_report(columna, columnb, columnc, columnd)
values (report_tab.getA, report_tab.getB, report_tab.getC, report_tab.getD);
After all i run report_pkg.get_data for each member function. It brings performance issues out. How can i improve the performance and what is the correct use of user defined type member functions?
Thank you.
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;
/
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.
I'm having this annoying issue with aggregate udfs where it always fails to invoke the iteration method. My code is just like the other examples around the web (including the ones on oracle docs and asktom). I tried to change the UDF type and the same happens everytime. It says:
ORA-29925: cannot execute DBAODB.WMCONCATROUTINES.ODCIAGGREGATEITERATE
ORA-06553: PLS-306: wrong number or types of arguments in call to 'ODCIAGGREGATEITERATE'
Oracle version is 11.1.0.7.0 and Here's my code:
CREATE OR REPLACE TYPE WmConcatRoutines as object (
tmpData number,
STATIC FUNCTION ODCIAggregateInitialize( wmInst IN OUT WmConcatRoutines) RETURN number,
MEMBER FUNCTION ODCIAggregateIterate(wmInst IN OUT WmConcatRoutines, value number) return number,
MEMBER FUNCTION ODCIAggregateMerge(wmInst IN OUT WmConcatRoutines, wmInst2 IN WmConcatRoutines) return number,
MEMBER FUNCTION ODCIAggregateTerminate(wmInst IN WmConcatRoutines, returnValue OUT number, flags IN number) return number
);
/
CREATE OR REPLACE TYPE BODY WmConcatRoutines IS
STATIC FUNCTION ODCIAggregateInitialize( wmInst IN OUT WmConcatRoutines) RETURN number IS
BEGIN
wmInst := WmConcatRoutines(0);
return ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateIterate(wmInst IN OUT WmConcatRoutines, value number) return number IS
BEGIN
wmInst.tmpData := wmInst.tmpData + value;
return ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateTerminate(wmInst IN WmConcatRoutines, returnValue OUT number, flags IN number) return number IS
BEGIN
returnValue := wmInst.tmpData;
return ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateMerge(wmInst IN OUT WmConcatRoutines, wmInst2 IN WmConcatRoutines) return number IS
BEGIN
wmInst.tmpData := wmInst.tmpData + wmInst2.tmpData;
return ODCIConst.Success;
END;
END;
/
CREATE OR REPLACE FUNCTION WM_CONCAT_test (input number) RETURN number
parallel_enable
AGGREGATE USING WmConcatRoutines;
/
Any ideas on what may be causing this? thanks in advance
The parameter names in your implementations of the ODCI functions have to match those in the documentation; so you have to use sctx, self, and ctx2 rather than wminst and wminst2.
Edit As APC briefly mentioned, it only seems to be self that is required; you can use something else in place of sctx and ctx2. I can't find a documentation reference...
Edit 2 Yes, I can... APC's reference to it being an object thing led me to this.