to test the code: https://dbfiddle.uk/?rdbms=oracle_21&fiddle=1be9f3d406df287afd874ad5dfc94cfc
I would like to have constant field inside a object. I can do that with a package but I haven't succeeded to do that with an object. Why?
create type a as object(
a integer
) --OK
create type as object(
b constant integer :=1
)
ORA-02302: invalid or missing type name
The CREATE TYPE syntax does not allow it:
object_type_def ::=
datatype ::=
Nowhere does it say that you can use the CONSTANT keyword.
As an alternative, you can create an attribute and set it to a value in a user-defined constructor:
CREATE TYPE test IS OBJECT(
a NUMBER,
pi NUMBER,
CONSTRUCTOR FUNCTION test(
SELF IN OUT NOCOPY test,
a NUMBER
) RETURN SELF AS RESULT
);
with the type body:
CREATE TYPE BODY test IS
CONSTRUCTOR FUNCTION test(
SELF IN OUT NOCOPY test,
a NUMBER
) RETURN SELF AS RESULT
IS
BEGIN
SELF.a := a;
SELF.pi := 3.14159;
RETURN;
END;
END;
/
Then:
SELECT test(1).a, test(1).pi
FROM DUAL;
Outputs:
TEST(1).A
TEST(1).PI
1
3.14159
However, that does not make the value a constant and it can still be set to something else:
DECLARE
v_obj TEST := TEST(1);
BEGIN
v_obj.pi := 4; -- For large circles!
DBMS_OUTPUT.PUT_LINE( v_obj.a || ', ' || v_obj.pi );
END;
/
Outputs:
1, 4
As long as you have a policy of never changing the value then it will be the default from the user-defined constructor; if someone breaks that policy then...
db<>fiddle here
Related
I have the following object :
-- A dummy type with one attribute
CREATE OR REPLACE TYPE example_type AS OBJECT
(
value VARCHAR2(50),
CONSTRUCTOR FUNCTION example_type (value_in VARCHAR2) RETURN SELF AS RESULT
);
/
CREATE OR REPLACE TYPE BODY example_type AS
CONSTRUCTOR FUNCTION example_type (value_in VARCHAR2) RETURN SELF AS RESULT IS
BEGIN
SELF.value := value_in;
RETURN;
END;
END;
/
And another table type of the same object :
-- A table of example_type
CREATE OR REPLACE TYPE example_table_type AS TABLE OF example_type;
/
I use these two objects as follows :
declare
Item example_type;
ItemsList example_table_type;
Value1 varchar2(15);
Value2 varchar2(15);
begin
Item := example_type(value_in => 'a');
ItemsList := example_table_type();
ItemsList.extend(1);
ItemsList(ItemsList.count) := Item;
ItemsList(1).value := 'b'; -- Replace the object value with 'b'
Value1 := Item.value; -- This returns 'a'. Why hasn't this value changed?
Value2 := ItemsList(1).value; -- This returns 'b'
end;
When running this code the value of the original object doesn't change (It's still a) While I'm expecting it to change to b.
If I run this code in C# or .Net it will change.
I was wondering why the value remains the same as I expect Oracle to "pass the object by reference".
Does anyone know what am I doing wrong?
Thanks.
I was wondering why the value remains the same as I expect Oracle to "pass the object by reference".
Does anyone know what am I doing wrong ?
Oracle does not "pass by reference".
declare
item1 example_type := example_type(value_in => 'a');
item2 example_type := item1;
begin
item1.value := 'b';
DBMS_OUTPUT.PUT_LINE(
item1.value || ' - ' || item2.value
);
end;
/
Outputs b - a as item2 is a copy of item1 and not a reference to item1.
If you want to create references to objects then store them in a table and use a REF pointer.
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'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 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);