How can I delete a member function created inside oracle type object without dropping the entire type?
I never used it but according documentation you might be able to use
alter type {type_name} DROP MEMBER {function_spec} CASCADE;
Note, {function_spec} is the function name plus RETURN clause, e.g.
ALTER TYPE ttest DROP MEMBER FUNCTION f_name_2 RETURN VARCHAR2 CASCADE;
Afterwards you have to re-create the TYPE BODY without the dropped function, i.e. CREATE OR REPLACE TYPE BODY ...
As far as I can tell, you can't (unless you call CREATE OR REPLACE to be not dropping).
P.S. What's even worse, if there are dependent types that use the type with a member function you'd want to drop, you'll need to drop everything cascade. Which means that - as a function doesn't have to eat or drink - leave it alone.
[EDIT; after seeing Wernfried's answer]
It seems that I was wrong. Apparently, it really works. Syntax is somewhat strange (it requires to specify the RETURN clause (?!?)), but hey - that's what Oracle says. So, today I learnt something new.
An example:
SQL> create or replace type ttest is object
2 (some_name varchar2(20),
3 member function f_name return varchar2
4 );
5 /
Type created.
SQL> create or replace type body ttest as
2 member function f_name
3 return varchar2
4 is
5 begin
6 return self.some_name;
7 end f_name;
8 end;
9 /
Type body created.
SQL> alter type ttest drop member function f_name return varchar2;
Type altered.
SQL> desc ttest
Name Null? Type
----------------------------------------- -------- ----------------------------
SOME_NAME VARCHAR2(20)
SQL>
Related
I'm trying to compile this procedure. I whish it gets table row as parameter:
create or replace PROCEDURE MY_HELPER (rep_table_row IN OUT rep_table_T%ROWTYPE) IS
...
END MY_HELPER ;
The table is defined as
create or replace TYPE "rep_table_T" AS TABLE OF rep_table_O;
The object is defined as:
create or replace TYPE "rep_table_O" AS OBJECT (
"day" VARCHAR2(250 BYTE),
"TS" DATE
);
However I can't compile it because I'm getting the error:
PLS-00201 identifier "rep_table_T" must be declared.
Remove the %ROWTYPE. That only applies to actual sql tables, not pl/sql collections. From the documentation, "The %ROWTYPE attribute provides a record type that represents a row in a database table. "
Yeah it is very confusing as a new user that oracle calls actual tables and pl/sql tables the same thing.
Forget about double quotes while in Oracle. If you use them while creating any objects, you have to use them always.
As of your code: removed double quotes, removed rowtype in procedure declaration.
SQL> CREATE OR REPLACE TYPE rep_table_o AS OBJECT
2 (
3 day VARCHAR2 (250 BYTE),
4 ts DATE
5 );
6 /
Type created.
SQL> CREATE OR REPLACE TYPE rep_table_t AS TABLE OF rep_table_o;
2 /
Type created.
SQL> CREATE OR REPLACE PROCEDURE my_helper (
2 rep_table_row IN OUT rep_table_t)
3 IS
4 BEGIN
5 NULL;
6 END my_helper;
7 /
Procedure created.
SQL>
You have two errors:
Do not use quoted identifiers:
CREATE TYPE rep_table_O AS OBJECT (
day VARCHAR2(250 BYTE),
TS DATE
);
CREATE TYPE rep_table_T AS TABLE OF rep_table_O;
(Or, if you really must have lower-case identifiers [why?] then you need to use quoted identifiers, with exactly the same case, everywhere that identifier is used.)
Do not use %ROWTYPE:
CREATE PROCEDURE MY_HELPER (
rep_table_row IN OUT rep_table_T
)
IS
...
END MY_HELPER;
db<>fiddle here
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.
I am defining a MEMBER FUNCTION for this type:
CREATE OR REPLACE TYPE HITO_T (
nombre VARCHAR2 (20) ,
categoria VARCHAR2 (20) ,
estado VARCHAR2 (10) ,
costo_entrada NUMBER (10,0),
zonas ZONA_TABLE_T,
MEMBER FUNCTION listar_zonas RETURN ZONA_T
);
/
The type has an attribute for a nested table and the function must return all the values for that nested table, so what I have is:
CREATE OR REPLACE TYPE BODY HITO_T as
MEMBER FUNCTION listar_zonas RETURN VARCHAR2 is
BEGIN
return self.zonas
END listar_zonas;
END;
/
The definitions for ZONA_T and ZONA_TABLE_T are:
CREATE OR REPLACE TYPE ZONA_T UNDER LUGAR_TIPO (
nombre VARCHAR2(20),
tamano NUMBER,
poligonos POLIGONO_TABLE,
MEMBER FUNCTION listar_poligonos RETURN POLIGONO_T);
/
CREATE OR REPLACE TYPE ZONA_TABLE_T AS TABLE OF ZONA_T;
/
I can't seem to start trying stuff since I don't know how to call the function. My specific question is how to call the function to test it with actual objects on the database, instead of using one defined at the moment (that is, CALL HITO_T('a','b','c'...)... will not work because the function needs values from the zonas attribute, which is a nested table and it can't be defined at the moment just like that). Futhermore I am not sure I can just return «self.zonas» just like that.
Any ideas?
The VALUE statement was what I was looking for.
Before submitting this question I've checked the Oracle docs about this subject:
Using CREATE OR REPLACE TYPE with Type and Table Dependencies
but still don't get my error solved.
I've created some object types:
CREATE OR REPLACE TYPE SchoolMember AS OBJECT (
...
) NOT FINAL;
/
CREATE OR REPLACE TYPE Teacher UNDER SchoolMember (
...
);
/
CREATE OR REPLACE TYPE Curse AS OBJECT (
...
refTeacher REF Teacher,
...
);
/
Let's say I want to modify the Teacher object type adding a constructor:
CREATE OR REPLACE TYPE Teacher UNDER SchoolMember (
...
CONSTRUCTOR FUNCTION Teacher(...) RETURN SELF AS RESULT
);
/
CREATE OR REPLACE TYPE BODY Teacher AS
...
END;
/
I get the ORA-02303: cannot drop or replace a type with type or table dependents because the Teacher type is used in the Curse object type itself.
Where should I place the FORCE option when modifiying an inherited object type (Teacher in this case)?
I've tried several ways, but none of them working:
CREATE OR REPLACE TYPE Teacher FORCE UNDER SchoolMember
or
CREATE OR REPLACE TYPE Teacher UNDER SchoolMember FORCE
But the ORA-02303 error is still there
I cannot exactly reproduce your issue.
Original Objects
SQL> CREATE OR REPLACE TYPE SchoolMember AS OBJECT (a number) NOT FINAL;
2 /
Type created.
SQL> CREATE OR REPLACE TYPE Teacher UNDER SchoolMember (b number);
2 /
Type created.
SQL> CREATE OR REPLACE TYPE Curse AS OBJECT (refTeacher REF Teacher);
2 /
Type created.
Create without FORCE fails
SQL> CREATE OR REPLACE TYPE Teacher UNDER SchoolMember (
2 b number,
3 CONSTRUCTOR FUNCTION Teacher RETURN SELF AS RESULT
4 );
5 /
CREATE OR REPLACE TYPE Teacher UNDER SchoolMember (
*
ERROR at line 1:
ORA-02303: cannot drop or replace a type with type or table dependents
Create with FORCE works
SQL> CREATE OR REPLACE TYPE Teacher FORCE UNDER SchoolMember (
2 b number,
3 CONSTRUCTOR FUNCTION Teacher RETURN SELF AS RESULT
4 );
5 /
Type created.
Update
The FORCE option for CREATE TYPE was added in 11gR2. You can see it in this 11gR2 syntax diagram, but not in this 11gR1 diagram.
In your case, you will need to drop the type before you recreate it.
SQL> DROP TYPE Teacher VALIDATE;
Type dropped.
Drop the type with force option and execute your code. It will work
DROP TYPE Teacher VALIDATE FORCE;
SQL> CREATE OR REPLACE TYPE Teacher UNDER SchoolMember (
b number,
CONSTRUCTOR FUNCTION Teacher RETURN SELF AS RESULT
);
Type created.
Is there any way to create an implode routine in PL/SQL that takes any custom datatype as a parameter and concatenates its members, delimited by some specified string?
For example, say I've got the following type:
CREATE TYPE myPerson AS OBJECT(
forename VARCHAR2(50),
surname VARCHAR2(50),
age NUMBER
);
Then, say a function returns an object of type myPerson, but I want the columns concatenated together:
SELECT implode(getPerson(1234),'$$') from dual;
to return (supposing the data in this contrived example is set up):
John$$Doe$$55
Where the delimiter can be specified as an optional parameter, but the type of the first parameter could be anything (not necessarily myPerson).
Your custom datatype can support methods and methods can have parameters.
CREATE TYPE myPerson AS OBJECT(
forename VARCHAR2(50),
surname VARCHAR2(50),
age NUMBER,
MEMBER FUNCTION
get_record(pGlue IN varchar2) RETURN VARCHAR2 );
CREATE TYPE BODY myPerson
AS
MEMBER FUNCTION get_record(pGlue varchar2) RETURN VARCHAR2
IS
BEGIN
RETURN forename || pGlue || surname || pGlue || age ;
END get_record;
END;
It is possible to build a generic way of handling these strings, by using inheritance and polymorphism. If we're going to use objects, we should leverage the capabilities of object-orineted programming.
Firstly we need a root object. This TYPE is not instantiable, which means we cannot actually declare an instance of it. Note that the TO_STRING() member function is also declared as NOT INSTANTIABLE. This means any TYPE which inherits from STRINGABLE_TYPE must have its own implementation of the method.
SQL> create or replace type stringable_type as object
2 ( id number(7,0)
3 , NOT INSTANTIABLE member function to_string
4 return varchar2
5 )
6 not final not instantiable
7 /
Type created.
SQL>
Here is one type which inherits from STRINGABLE_TYPE. The OVERRIDING keyword is mandatory, even though the declartion of the parent type compels us to implement it.
SQL> create or replace type emp_type under stringable_type
2 ( empno number(7,0)
3 , ename varchar2(20)
4 , sal number(7,2)
5 , OVERRIDING member function to_string
6 return varchar2
7 );
8 /
Type created.
SQL> create or replace type body emp_type
2 is
3 OVERRIDING member function to_string
4 return varchar2
5 is
6 begin
7 return 'EMP>>'||self.id||'='||self.empno||'::'||self.ename||'::'||self.sal;
8 end;
9 end;
10 /
Type body created.
SQL>
Here is another type...
SQL> create or replace type dept_type under stringable_type
2 ( deptno number(2,0)
3 , dname varchar2(30)
4 , OVERRIDING member function to_string
5 return varchar2
6 );
7 /
Type created.
SQL> create or replace type body dept_type
2 is
3 OVERRIDING member function to_string
4 return varchar2
5 is
6 begin
7 return 'DEPT>>'||self.id||'='||self.deptno||'::'||self.dname;
8 end;
9 end;
10 /
Type body created.
SQL>
Now, we can create a function which takes the generic type and invokes the generic method:
SQL> create or replace function type_to_string
2 (p_obj in stringable_type)
3 return varchar2
4 is
5 begin
6 return p_obj.to_string();
7 end;
8 /
Function created.
SQL>
Through the wonders of polymorphism we can pass two different objects to the function, which will actually execute the overriding method:
SQL> set serveroutput on
SQL> declare
2 obj1 emp_type;
3 obj2 dept_type;
4 begin
5 obj1 := emp_type(1, 8000, 'VAN WIJK', 3500);
6 obj2 := dept_type(2, 20, 'COMMUNICATIONS');
7 dbms_output.put_line(type_to_string(obj1));
8 dbms_output.put_line(type_to_string(obj2));
9 end;
10 /
EMP>>1=8000::VAN WIJK::3500
DEPT>>2=20::COMMUNICATIONS
PL/SQL procedure successfully completed.
SQL>
It's quite a lot of work to get to this point. It would be neat if Oracle's TYPE at least had an abstract TO_STRING() baked into it, which we could just override. But that is just one of many loose ends in their object implementation 8-)