I'm trying to create object data types in Oracle.
I have an object type called type_transaction, I use this type of body
EXTENDS functions.
The compiler crashes the following error:
"identifier 'EXTENDS' must be declared.
I do not know what I'm doing wrong
This is type :
CREATE OR REPLACE TYPE type_transakcja AS OBJECT
(
id_transakcji NUMBER(10),
data_transakcji DATE,
pracownika_id REF type_pracownik,
klient_id REF type_klienta,
produkt_id REF type_produkt,
zamowienia k_zamowienie,
MAP MEMBER FUNCTION odwzoruj RETURN NUMBER,
MEMBER FUNCTION wartosc_zamowienia RETURN NUMBER,
MEMBER FUNCTION transakcja(produkty REF type_produkt, ile NUMBER)
RETURN NUMBER,
MEMBER PROCEDURE anuluj,
MEMBER PROCEDURE zatwierdz
);
CREATE TYPE k_zamowienie AS TABLE OF type_sprzedaz;
and this is body of type
CREATE OR REPLACE TYPE BODY type_transakcja IS
MEMBER FUNCTION transakcja(produkty REF type_produkt, ile NUMBER)
RETURN NUMBER IS
i INTEGER;
t k_zamowienie;
p type_produkt;
BEGIN
SELECT DEREF(produkty) INTO p FROM DUAL;
IF p.ilosc_magazynowa - ile >=0 THEN
p.ilosc_magazynowa:=p.ilosc_magazynowa-ile;
t:=SELF.zamowienia;
t:=extend(1);
i:= SELF.zamowienia.last();
t(i).ilosc:=ile;
t(i).cena:=SELF.wartosc_zamowienia();
t(i).produkt_id:=produkty;
RETURN 1;
ELSE
RETURN 0;
END IF;
END;
END;
It's hard to be sure from the limited information you've provided, and what seems to be not quite the actual error you get; but it looks like you're just extending your collection incorrectly.
Assuming k_zamowienie is a table type, you're doing:
t:=extend(1);
instead of:
t.extend(1);
And you don't really need i, and if you do have it your index is going to be one off (as it's based on the size of the original table, not the cloned-and-extended on); you can do:
t(t.last).ilosc:=ile;
t(t.last).cena:=SELF.wartosc_zamowienia();
t(t.last).produkt_id:=produkty;
But even then you aren't doing anything with t, so this seems a bit pointless. Maybe you meant this?
zamowienia.extend(1);
zamowienia(zamowienia.last()).ilosc:=ile;
zamowienia(zamowienia.last()).cena:=SELF.wartosc_zamowienia();
zamowienia(zamowienia.last()).produkt_id:=produkty;
Related
I have the following plsql object :
CREATE OR REPLACE TYPE TestType AS OBJECT
(
firstname VARCHAR2(30),
lastname VARCHAR2(30),
MEMBER PROCEDURE dosomething,
MEMBER function downgrade return number,
CONSTRUCTOR FUNCTION TestType(fname VARCHAR2, lname VARCHAR2) RETURN SELF AS RESULT
)
/
CREATE OR REPLACE TYPE BODY TestType AS
CONSTRUCTOR FUNCTION TestType(fname VARCHAR2, lname VARCHAR2)
RETURN SELF AS RESULT
IS
BEGIN
SELF.firstname := fname;
SELF.lastname := lname;
RETURN;
END;
MEMBER function downgrade return number IS
BEGIN
self.dosomething; /* Compilation Error Here */
return 1;
END;
MEMBER PROCEDURE dosomething IS
BEGIN
null;
END;
END;
/
It's an example of a complex object I have. Basically from a member function, I need to call a member procedure. When doing so, I'm getting the compilation error : PLS-00363: expression cannot be used as an assignment target
I don't get why this error is raised.
Does anyone know what am I doing wrong please ?
Thanks.
Cheers,
Although the documentation says:
Member methods have a built-in parameter named SELF that denotes the object instance currently invoking the method.
SELF can be explicitly declared, but that is not necessary. ...
it also later says:
SELF is always the first parameter passed to the method.
In member functions, if SELF is not declared, its parameter mode defaults to IN.
In member procedures, if SELF is not declared, its parameter mode defaults to IN OUT. The default behavior does not include the NOCOPY compiler hint.
You're getting the error because your procedure call
self.dosomething;
is implicitly passing self to the procedure as IN OUT, but it's implicitly passed into the function as IN. That means that it isn't in the right mode for that procedure call - the mode means the procedure could modify self, while the function says it won't.
You can avoid the error by changing the mode in the function, by explicitly declaring the self argument, in both the type:
MEMBER function downgrade (self in out TestType) return number,
and type body:
MEMBER function downgrade (self in out TestType) return number IS
fiddle
If your real procedure won't modify self (maybe unlikely) then you could declare it's self IN instead.
It is often said that functions should only return values and shouldn't have side-effects, so arguably that would be more correct - someone calling your member function might not expect it to change anything.
So if dosomething is changing data then perhaps downgrade should also be a procedure, with an OUT argument instead of a return value. You then wouldn't need to declare self as both would be IN OUT by default.
This one compiles successfully:
MEMBER FUNCTION DOWNGRADE RETURN NUMBER IS
me TestType := SELF;
BEGIN
me.dosomething;
RETURN 1;
END;
But you should test it carefully, whether it is working as expected.
Otherwise you could change the procedure into a function and ignore the return value:
CREATE OR REPLACE TYPE TestType AS OBJECT
(
firstname VARCHAR2(30),
lastname VARCHAR2(30),
MEMBER FUNCTION dosomething RETURN NUMBER,
MEMBER FUNCTION DOWNGRADE RETURN NUMBER,
CONSTRUCTOR FUNCTION TestType(fname VARCHAR2, lname VARCHAR2) RETURN SELF AS RESULT
)
/
CREATE OR REPLACE TYPE BODY TestType AS
CONSTRUCTOR FUNCTION TestType(fname VARCHAR2, lname VARCHAR2)
RETURN SELF AS RESULT
IS
BEGIN
SELF.firstname := fname;
SELF.lastname := lname;
RETURN;
END;
MEMBER FUNCTION DOWNGRADE RETURN NUMBER IS
ret NUMBER;
BEGIN
ret := self.dosomething;
RETURN 1;
END;
MEMBER FUNCTION dosomething RETURN NUMBER IS
BEGIN
NULL;
RETURN NULL;
END;
END;
/
Another approach could be to create a STATIC PROCEDURE procedure instead of a MEMBER PROCEDURE
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'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;
/
one of my assignment question is:
The method AvgReviewScore() returns the average review score for an album excluding scores from anonymous reviewers, i.e. reviews with null REVIEWER_NAME.
So firstly i wrote SQL:
(This is oriented database assignment)
select deref(b.album).title
,round(avg(b2.reviewscore),2)
from album_artist_table b,table(deref(b.album).review) b2
where deref(b.artist).aname like '%Joe%'
and b2.reviewername is not null
group by deref(b.album).title;
Can i ask how to translate this sql to member function?
I try to create a type called:AvgReviewScore_type
and then i create type body member function as following:
create or replace TYPE BODY ALBUM_TYPE AS
member function AvgReviewScore return AvgReviewScore_type AS
AVGtable AvgReviewScore_type := AvgReviewScore_type(null,null);
BEGIN
select deref(b.album).title,round(avg(b2.reviewscore),2)
into AVGtable
from album_artist_table b,table(deref(b.album).review) b2
where b2.reviewername is not null
group by deref(b.album).title;
return AVGtable;
END AvgReviewScore;
END;
But it doesn't work, is there anybody know about the reason?
I guess your question has incomplete information and depending on whatever given see the below example. I hope it would help you.
Create type exmpl_type as object (
num number,
member function func(p in number) return number
);
/
create type body exmpl_type as
member function func(p in number)
return number is
begin
return num/p;
end func;
end;
/
I am trying to print an table type for debugging purposes, but don't know how. I tried the following two methods, neither of which work:
dbms_output.put_line (V_TEMP_TABTYPE(1));
dbms_output.put_line (V_TEMP_TABTYPE);
The error generated is: PLS-00306: wrong number or types of arguments in call to.
So, how can I print the contents of a table type? Or is there a different way to display the contents?
The table_type and the type it references are::
create or replace TYPE MY_TYPE IS OBJECT( MyString Varchar(20)
, counter Number(9) );
create or replace TYPE MY_TABTYPE AS TABLE OF MY_TYPE;
Oracle has objects but it's ... different. Not exactly sure with your question if you want to see the values of the properties or if you want to actually see the type.
CREATE OR REPLACE TYPE MY_TYPE IS OBJECT (
MyString Varchar(20)
, counter Number(9)
);
Now run some code for it.
DECLARE
myType MY_TYPE;
BEGIN
myType := MY_TYPE('ABC123',0);
-- To see the values reference the properties
DBMS_OUTPUT.PUT_LINE(myType.mystring);
-- To see they TYPE of the OBJECT
DBMS_OUTPUT.PUT_LINE(SYS.ANYDATA.CONVERTOBJECT(myType).getTypeName());
END;
Of course you can create methods on the object to return information for you a bit easier.
CREATE OR REPLACE TYPE MY_TYPE IS OBJECT (
MyString Varchar(20)
, counter Number(9)
, MEMBER FUNCTION getType RETURN VARCHAR2
, MEMBER FUNCTION toString RETURN VARCHAR2
)
/
CREATE OR REPLACE TYPE BODY MY_TYPE
AS
MEMBER FUNCTION getTYPE RETURN VARCHAR2 IS
BEGIN
RETURN SYS.ANYDATA.CONVERTOBJECT(SELF).getTypeName();
END;
MEMBER FUNCTION toString RETURN VARCHAR2 IS
BEGIN
RETURN 'MY_TYPE('||self.mystring||','||self.counter||')';
END;
END;
/
You can call the functions on the object now makes it easier to read imo.
DECLARE
mytype MY_TYPE;
BEGIN
mytype := MY_TYPE('AGAIN','0');
DBMS_OUTPUT.PUT_LINE(mytype.toString);
DBMS_OUTPUT.PUT_LINE(mytype.getType);
END;
dbms_output.put_line(v_temp_tabtype(i).myString);