I have an object type like below:
CREATE OR REPLACE TYPE MYOBJ AS OBJECT( Att VARCHAR2(200) );
I wish to have some constants attributes there. How should I program it? I mean I wish to have something like:
Att CONSTANT VARCHAR2(200) := 'Something'
Can I do it in spec? Or in constructor? Is it possible at all?
You cannot have a CONSTANT attribute of an object.
There are two alternate methods you can take:
initialise a non-constant value in the constructor (and never change it); or
create a static function:
CREATE OR REPLACE TYPE MYOBJ AS OBJECT(
Att VARCHAR2(200),
CONSTRUCTOR FUNCTION myobj RETURN SELF AS RESULT,
STATIC FUNCTION const_value RETURN VARCHAR2
);
CREATE OR REPLACE TYPE BODY MYOBJ AS
CONSTRUCTOR FUNCTION myobj RETURN SELF AS RESULT
IS
BEGIN
SELF.att := 'ABC';
RETURN;
END;
STATIC FUNCTION const_value RETURN VARCHAR2
IS
BEGIN
RETURN 'ABC';
END;
END;
/
Then:
SELECT MYOBJ().ATT FROM DUAL;
Creates an instance of the object, initialises the att attribute in the constructor and then returns it. However, the value is not a constant so it is possible to modify it (but if you never change it then it will be effectively constant).
Or you can call the static function:
SELECT MYOBJ.const_value() FROM DUAL;
Which will be constant but it is not an attribute of the object.
db<>fiddle here
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'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'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;
/
help me please to understand this error..
May be i must do ud_mosh_dvig(x number, y number) and create type body after.
create or replace type CAR as object
(
mosh_dvig number,
obiem_dvig number,
color varchar2(20),
type_form varchar2(20),
massa number,
num_peredach number,
ud_mosh number,
member function ud_mosh_dvig return number
);
create or replace type body CAR
is
member function ud_mosh_dvig return number
as
begin
self.ud_mosh:=self.mosh_dvig/self.massa;
return self.ud_mosh;
end ud_mosh_dvig;
end;
By default, for every non-static function, implicitly declared self parameter is in IN parameter mode. It means, that it simply cannot be modified. But, it should be noted that for non-static procedures self parameter is in IN OUT default parameter mode.
Although it is not a good practice to allow a function to return multiple values, change value ud_mosh property of an object and return the same value to the invoker, in this case, you can explicitly declare self parameter of the function in IN OUT parameter mode:
create or replace type CAR as object (
mosh_dvig number,
obiem_dvig number,
color varchar2(20),
type_form varchar2(20),
massa number,
num_peredach number,
ud_mosh number,
member function ud_mosh_dvig(self in out car) return number
);
TYPE CAR compiled
create or replace type body CAR
is
member function ud_mosh_dvig(self in out car)
return number as
begin
self.ud_mosh := self.mosh_dvig/self.massa;
return self.ud_mosh;
end ud_mosh_dvig;
end;
TYPE BODY CAR compiled
But you wont be able to use that function in SQL, because of declared formal parameter of the function in IN OUT parameter mode - only PL/SQL
set serveroutput on;
clear screen;
declare
l_obj car;
l_obj1 car;
l_res number;
begin
l_obj := new car(1,1,'1','1',4,1,1);
l_res := l_obj.ud_mosh_dvig();
dbms_output.put_line('ud_mosh prop value: ' || l_obj.ud_mosh || chr(13)
|| 'Function returns: ' || to_char(l_res));
end;
/
anonymous block completed
ud_mosh prop value: 0.25
Function returns: 0.25
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);