Extend Oracle Collections with member functions - oracle

I am wondering wether it is possible to extend any type of collections (Associative Array,Nested Table, VArray) with custom functions.
I wish to be able to define custom functions in the same style I can do it for regular types using member functions. Using this, i would like to create a function which for example translates the content of my collection to a Character String by concating its items.

No, it is not possible.
What you can do is to encapsulate collection into another type like this
create or replace type my_array as table of varchar2(10);
/
create or replace type my_array_type as object (
arr my_array, member function do_something return varchar2)
/
create or replace type body my_array_type is
member function do_something return varchar2 is
l_temp varchar2(32767);
begin
for i in arr.first .. arr.last
loop
l_temp:=l_temp||arr(i);
end loop;
return l_temp;
end;
end;
/
Now you can try your concatenation function out:
declare
temp_array my_array:=my_array();
test_array my_array_type:=my_array_type(null);
result_string varchar2(32767);
begin
temp_array.extend(3);
temp_array(1):='a';
temp_array(2):='b';
temp_array(3):='c';
test_array:=my_array_type(temp_array);
result_string :=test_array.do_something;
dbms_output.put_line(result_string);
end;

As far as I'm aware Oracle does not provide a way to add methods or custom functions to a collection subtype. The alternatives I can think of are:
You could define a TYPE which wraps a collection and then define methods on the TYPE.
You could define a collection subtype in a package and then create procedures/functions in the package which manipulate the defined collection subtype.
Best of luck.

Related

difference between creating a type with new keyword or without in plsql

In oracle pl/sql you can create an object with or without the new keyword.
Is there any difference how oracle threats these requests ?
So for example:
CREATE TYPE emp_object AS OBJECT(
emp_no NUMBER,
emp_name VARCHAR2(50),
salary NUMBER,
manager NUMBER,
CONSTRUCTOR FUNCTION emp_object(p_emp_no NUMBER, p_emp_name VARCHAR2,
p_salary NUMBER) RETURN SELF AS RESULT),
MEMBER PROCEDURE insert_records,
MEMBER PROCEDURE display_records);
/
CREATE OR REPLACE TYPE BODY emp_object AS
CONSTRUCTOR FUNCTION emp_object(p_emp_no NUMBER,p_emp_name VARCHAR2,
p_salary NUMBER)
RETURN SELF AS RESULT
IS
BEGIN
Dbms_output.put_line('Constructor fired..');
SELF.emp_no:=p_emp_no;|
SELF.emp_name:=p_emp_name;
SELF.salary:=p_salary;
SELF.managerial:=1001;
RETURN;
END:
MEMBER PROCEDURE insert_records
IS
BEGIN
INSERT INTO emp VALUES(emp_noemp_name,salary,manager);
END
MEMBER PROCEDURE display_records
IS
BEGIN
Dbms_output.put_line('Employee Name:'||emp_name);
Dbms_output.put_line('Employee Number:'||emp_no);
Dbms_output.put_line('Salary':'||salary);
Dbms_output.put_line('Manager:'||manager);
END:
END:
/
This is a type with spec and body, now you can create the object like this without the new keyword:
DECLARE
guru_emp_det emp_object;
BEGIN
guru_emp_det:=emp_object(1005,'RRR',20000,1000);
guru_emp_det.display_records;
guru_emp_det.insert_records;
COMMIT;
END;
But it is also possible with the keyword:
DECLARE
guru_emp_det emp_object;
BEGIN
guru_emp_det:= new emp_object(1005,'RRR',20000,1000);
guru_emp_det.display_records;
guru_emp_det.insert_records;
COMMIT;
END;
From the Type Constructor Expressions documentation:
Type Constructor Expressions
A type constructor expression specifies a call to a constructor method. The argument to the type constructor is any expression. Type constructors can be invoked anywhere functions are invoked.
type_constructor_expression::=
The NEW keyword applies to constructors for object types but not for collection types. It instructs Oracle to construct a new object by invoking an appropriate constructor. The use of the NEW keyword is optional, but it is good practice to specify it.
As to your question:
Is there any difference how oracle treats these requests?
No, there is no difference; the NEW keyword is optional.
Although the documentation states that the NEW keyword does not apply for collection types, in practice it does not appear to be invalid syntax and is both allowable and optional for collection types (there may be an edge-case I have not yet found where the NEW keyword is forbidden; however, on some brief tests I cannot find such a case).
fiddle

How can i use REF type in PL/SQL Oracle?

I have this code :
CREATE OR REPLACE TYPE t_abonnement_type AS OBJECT
(
ref_abonnement_type NUMBER,
type_abonne VARCHAR(50),
MEMBER PROCEDURE DISPLAY
);
CREATE OR REPLACE TYPE t_abonnement AS OBJECT
(
ref_abonnement NUMBER,
date_debut DATE,
type_abonnement REF t_abonnement_type,
MEMBER PROCEDURE DISPLAY
);
What i want to do is just create the members procedures DISPLAY declared.
So i did it this way :
CREATE OR REPLACE TYPE BODY t_abonnement AS
MEMBER PROCEDURE DISPLAY IS
BEGIN
/* SOME CODE */
type_abonnement.display;
END;
END;
And i get this error
PLS-00536: Navigation through REF variables is not supported in PL/SQL.
So how can i deal with REF in PL/SQL statements ?
Thanks
As mentioned by #APC, it's not possible to directly use member function of a Object to another using REF since Oracle supports them in SQL but not in PL/SQL.
However if I look at your requirement from a different angle, I could see you are trying to simply make use of Procedure used in an Object to another Object. Means if I forget the referencing part and create a scenario which could satisfy the referencing concept then it should "OK".
This is possible. Yes..!!! This can be done. The only thing I did here is to make the two Objects as parent-child to achieve referencing. See demo below:
--Parent Object
CREATE OR REPLACE TYPE t_abonnement_type AS OBJECT
(
ref_abonnement_type NUMBER,
type_abonne VARCHAR(50),
MEMBER FUNCTION DISPLAY return varchar2
) NOT FINAL;
-- Member Funtion Body
CREATE OR REPLACE TYPE BODY t_abonnement_type
AS
MEMBER FUNCTION DISPLAY
return varchar2
IS
BEGIN
return ('Hi');
END DISPLAY;
END;
Testing my Parent Object:
SQL> SELECT t_abonnement_type(1,'a').display() col from DUAL;
COL
---
Hi
Child Object to achieve referencing concept
CREATE OR REPLACE TYPE t_abonnement
under t_abonnement_type
(
ref_abonnement NUMBER,
date_debut DATE,
MEMBER function V_DISPLAY return varchar2
);
--Member function
CREATE OR REPLACE TYPE BODY t_abonnement
AS
MEMBER Function V_DISPLAY return varchar2
IS
var varchar2(10);
BEGIN
var:= t_abonnement_type(1,'A').display(); --Calling Parent Member function here
return('Called from Child Object -->'||var);
END;
END;
Testing my Child Object to check if the parent Member function is referenced or not:
SQL> SELECT T_ABONNEMENT(1,'A',2,TO_DATE('30-JUN-2018','DD-MON-YYYY')).V_DISPLAY() COL FROM DUAL;
Col
---
Called from Child Object -->Hi
Note I haven't used overloading done in your code as I feel overloading makes the stuff harder to understand as an individual as well as for compiler.

PLS-00307 when subprograms differ only in type parameters (RAW vs VARCHAR2)

I have a package with two methods:
create or replace package demo
as
function overloaded(p_in varchar2)
return pls_integer;
function overloaded(p_in raw)
return pls_integer;
end;
/
create or replace package body demo
as
function overloaded(p_in raw)
return pls_integer
is
begin
return 1;
end;
function overloaded(p_in varchar2)
return pls_integer
is
begin
return 2;
end;
end;
/
It compiles without any errors, but I cannot call either method, as in both cases I get error PLS-00307: too many declarations match this call. Why does this happen with RAW and VARCHAR2? How can I work around this limitation? Is giving different names to my subprograms the only way out?
The documentation says:
PL/SQL lets you overload nested subprograms, package subprograms, and type methods. You can use the same name for several different subprograms if their formal parameters differ in name, number, order, or data type family.
And according to the the appendix that refers to, 'varchar2 and raw are both members of the char data type family.
You can give the subprograms different names, but you can also change the name and order of the formal parameters; in this case as there is only one parameter that means you can change only the name(s):
create or replace package demo
as
function overloaded(p_in_vc varchar2)
return pls_integer;
function overloaded(p_in_raw raw)
return pls_integer;
end;
/
(and the same change in the body of course); and then call with named notation for the actual parameters:
select demo.overloaded(p_in_vc=>'test') from dual;
DEMO.OVERLOADED(P_IN_VC=>'TEST')
--------------------------------
2
select demo.overloaded(p_in_raw=>'AABB') from dual;
DEMO.OVERLOADED(P_IN_RAW=>'AABB')
---------------------------------
1

How to pass an array type in plsql package specification?

I am new to plsql. I am trying to put two scripts under the same package. These scripts deal with arrays. How do I pass an array into the procedure? If I am to declare the array, do I do it in the specification or the body? I am trying this right now but it doesn't work.
CREATE PACKAGE cop_cow_script AS
PROCEDURE COP_COW_DATALOAD_V2(arr_claims VARRAY(15000) OF VARCHAR2(10), arr_sql VARRAY(500) OF VARCHAR2(1000));
END cop_cow_script;
As you see I want to pass in those two arrays.
You need to declare types in the package specification and use them as parameter types in the procedure declaration:
CREATE OR REPLACE PACKAGE cop_cow_script AS
TYPE arr_claims_t IS VARRAY(15000) OF VARCHAR2(10);
TYPE arr_sql_t IS VARRAY(500) OF VARCHAR2(1000);
PROCEDURE COP_COW_DATALOAD_V2(arr_claims arr_claims_t, arr_sql arr_sql_t);
END cop_cow_script;
/
EDIT - below is an example of the package body - the procedure loops through elements of it's first parameter and prints them using DBMS_OUTPUT.PUT_LINE
PROCEDURE COP_COW_DATALOAD_V2(arr_claims arr_claims_t, arr_sql arr_sql_t)
IS
BEGIN
FOR i IN arr_claims.FIRST .. arr_claims.LAST
LOOP
DBMS_OUTPUT.PUT_LINE( arr_claims( i ) );
END LOOP;
END;
END cop_cow_script;
/
An then you can initialize them and pass to the procedure invocation for example in this way (this is an anonymous block that declares two variables, initializes them and invokes the procedure passing both parameters to it:
DECLARE
my_array1 cop_cow_script.arr_claims_t := cop_cow_script.arr_claims_t();
my_array2 cop_cow_script.arr_sql_t := cop_cow_script.arr_sql_t();
BEGIN
my_array1.extend;
my_array1( 1 ) := 'string 1';
my_array2.extend;
my_array2( 1 ) := 'string 2';
cop_cow_script.COP_COW_DATALOAD_V2( my_array1, my_array2 );
END;
/
First off, I'm hard-pressed to imagine a situation where you'd actually want to use a PL/SQL varray rather than a nested table or an associative array. There really isn't a benefit to declaring a fixed size collection.
Whatever sort of collection type you want, you'd need to declare the collection type either in the package specification or at the SQL level (depending on the type of collection) separate from the procedure specification
CREATE PACKAGE cop_cow_script AS
TYPE arr_claims IS varray(15000) of varchar(10);
TYPE arr_sql IS varray(500) of varchar(1000);
PROCEDURE COP_COW_DATALOAD_V2(p_claims arr_claims,
p_sql arr_sql);
END cop_cow_script;

How to use Pl/SQL Collection within a Function

For an assignment the recommendation was to use collections. Simply said I need to chop a string in several substrings, reverse the order of those strings and put them all together.
Omitted actual code in because it crashes at the declare part.
Keep getting the error: stringlist1 is not a procedure or is undefined
So for some reason the type I try to assign to stringlist1 isn't doing it.
Do you guys know why this is happening? And if this isn't fixable what is a neat workaround other than making a variable for every row in the collection?
Things I tried:
* Create my own type in a "create type" statement beforehand
* Creating the type in an anonymous block before the function
* Creating a varchar collection within the declare part of the function
* The dbms package used below
* Renaming the hell out of it
* Trying to initiate the type like stringlist1 dbms_utility.name_array := dbms_utility.name_array();
Create or replace FUNCTION TEST (p_number varchar2)
RETURN varchar2
IS
stringlist1 dbms_utility.name_array;
stringlist2 dbms_utility.name_array;
BEGIN
stringlist1('test');
stringlist2('test');
dbms.output.put_line(stringlist1(1));
return stringlist1(1);
END;
String can be added to array like stringlist1(1) := 'test';
So your code will be compile when corrected like
CREATE OR REPLACE FUNCTION TEST (p_number VARCHAR2)
RETURN VARCHAR2
IS
stringlist1 DBMS_UTILITY.name_array;
stringlist2 DBMS_UTILITY.name_array;
BEGIN
stringlist1(1) := 'test';
stringlist2(1) := 'test';
dbms_output.put_line (stringlist1 (1));
RETURN stringlist1 (1);
END;
PS: Package name is not dmbs.output, correct one is dbms_output.

Resources