How to solve the error "PLS-00363: expression cannot be used as an assignment target" in Oracle? - oracle

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

Related

too many declaration of a construct match this call

I have declared an object with a constructor that takes 2 arguments. I can't use it because oracle returns:
[Error] Execution (2: 10): ORA-06553: PLS-307: too many declarations
of 'BOUNDARY' match this call
I haven't declared this constructor more than one time.
My code is working on db fiddle but not on my database. The only difference between the code on dbfiddle and my code is that this object has an owner.
Therefore the name of the object is defined this way: owner_name.object_name.
I have checked if the object is defined in another owner. And when I call the constructor I use owner_name.constructor.
I've created a dummy constructor that take 0 arguments and it works.
_
CREATE OR REPLACE TYPE my_user.boundary AS OBJECT
(
v_start INTEGER,
v_end INTEGER,
CONSTRUCTOR FUNCTION boundary (i_start INTEGER, i_end INTEGER)
RETURN SELF AS RESULT,
MEMBER FUNCTION isInside (i INTEGER)
RETURN INTEGER
);
CREATE OR REPLACE TYPE BODY my_user.boundary
AS
CONSTRUCTOR FUNCTION boundary
RETURN SELF AS RESULT
IS
BEGIN
v_start := 1;
v_end := 2;
RETURN;
END;
CONSTRUCTOR FUNCTION boundary (i_start INTEGER, i_end INTEGER)
RETURN SELF AS RESULT
IS
BEGIN
v_start := i_start;
v_end := i_end;
RETURN;
END;
MEMBER FUNCTION isInside (i INTEGER)
RETURN INTEGER
IS
BEGIN
IF v_start <= i AND i <= v_end
THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END;
END;
SELECT ( my_user.boundary(1,2)) FROM DUAL; --doesn't work
[Error] Execution (2: 10): ORA-06553: PLS-307: too many declarations of 'BOUNDARY' match this call
SELECT ( my_user.boundary()).isInside(1) FROM DUAL; --is working
1
Your code works in db<>fiddle when run under Oracle 21c, but not under 18c or 11g.
You said you haven't declared the constructor more than once, but you sort of have; there is a default constructor with the same parameters - from the 19c doc:
The database implicitly defines a constructor method for each user-defined type that you create. A constructor is a system-supplied procedure that is used in SQL statements or in PL/SQL code to construct an instance of the type value. The name of the constructor method is the name of the user-defined type. You can also create a user-defined constructor using the constructor_spec syntax.
and:
By default, the system implicitly defines a constructor function for all object types that have attributes.
A system-defined constructor is sometimes known as the attribute value constructor.
You are creating a user-defined constructor, but it has the same definition as the implicit one. Well, almost.
19c also says:
a user-defined constructor does hide, and thus supersede, the attribute-value constructor for its type if the signature of the user-defined constructor exactly matches the signature of the attribute-value constructor.
For the signatures to match, the names and types of the parameters (after the implicit SELF parameter) of the user-defined constructor must be the same as the names and types of the attributes of the type.
In 18c it works if you change the parameter names to match the attribute names:
CREATE OR REPLACE TYPE boundary AS OBJECT
(
v_start INTEGER,
v_end INTEGER,
CONSTRUCTOR FUNCTION boundary (v_start INTEGER, v_end INTEGER)
RETURN SELF AS RESULT,
MEMBER FUNCTION isInside (i INTEGER)
RETURN INTEGER
);
/
CREATE OR REPLACE TYPE BODY boundary
AS
CONSTRUCTOR FUNCTION boundary (v_start INTEGER, v_end INTEGER)
RETURN SELF AS RESULT
IS
BEGIN
SELF.v_start := v_start;
SELF.v_end := v_end;
RETURN;
END;
...
21c seems to be more forgiving/flexible, though the documentation hasn't changed. (Or maybe 19c, or some version of it, also allows this - I'm just going on the available versions in db<>fiddle..)
However, as all you are doing here is a simple assignment, you don't need your overriding constructor in any version - it works without it.
After I added the undeclared constructor to the spec it compiled and worked fine:
CREATE OR REPLACE TYPE BV_OWN.boundary AS OBJECT
(
v_start INTEGER,
v_end INTEGER,
CONSTRUCTOR FUNCTION boundary -- added
RETURN SELF AS RESULT,
CONSTRUCTOR FUNCTION boundary (i_start INTEGER, i_end INTEGER)
RETURN SELF AS RESULT,
MEMBER FUNCTION isInside (i INTEGER)
RETURN INTEGER
);
Had to drop the schema specs since I didn't have a BV_OWN user to test with. Used Oracle 21c. db<>fiddle here

Is there a way to allow an object function to return a variable without passing in that variable in the constructor?

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.

Setter methods in UDT in Oracle database

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;
/

create type. pls 00363 expression cannot be used as an assignment

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

Issue with Oracle user-defined aggregate functions

I'm having this annoying issue with aggregate udfs where it always fails to invoke the iteration method. My code is just like the other examples around the web (including the ones on oracle docs and asktom). I tried to change the UDF type and the same happens everytime. It says:
ORA-29925: cannot execute DBAODB.WMCONCATROUTINES.ODCIAGGREGATEITERATE
ORA-06553: PLS-306: wrong number or types of arguments in call to 'ODCIAGGREGATEITERATE'
Oracle version is 11.1.0.7.0 and Here's my code:
CREATE OR REPLACE TYPE WmConcatRoutines as object (
tmpData number,
STATIC FUNCTION ODCIAggregateInitialize( wmInst IN OUT WmConcatRoutines) RETURN number,
MEMBER FUNCTION ODCIAggregateIterate(wmInst IN OUT WmConcatRoutines, value number) return number,
MEMBER FUNCTION ODCIAggregateMerge(wmInst IN OUT WmConcatRoutines, wmInst2 IN WmConcatRoutines) return number,
MEMBER FUNCTION ODCIAggregateTerminate(wmInst IN WmConcatRoutines, returnValue OUT number, flags IN number) return number
);
/
CREATE OR REPLACE TYPE BODY WmConcatRoutines IS
STATIC FUNCTION ODCIAggregateInitialize( wmInst IN OUT WmConcatRoutines) RETURN number IS
BEGIN
wmInst := WmConcatRoutines(0);
return ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateIterate(wmInst IN OUT WmConcatRoutines, value number) return number IS
BEGIN
wmInst.tmpData := wmInst.tmpData + value;
return ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateTerminate(wmInst IN WmConcatRoutines, returnValue OUT number, flags IN number) return number IS
BEGIN
returnValue := wmInst.tmpData;
return ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateMerge(wmInst IN OUT WmConcatRoutines, wmInst2 IN WmConcatRoutines) return number IS
BEGIN
wmInst.tmpData := wmInst.tmpData + wmInst2.tmpData;
return ODCIConst.Success;
END;
END;
/
CREATE OR REPLACE FUNCTION WM_CONCAT_test (input number) RETURN number
parallel_enable
AGGREGATE USING WmConcatRoutines;
/
Any ideas on what may be causing this? thanks in advance
The parameter names in your implementations of the ODCI functions have to match those in the documentation; so you have to use sctx, self, and ctx2 rather than wminst and wminst2.
Edit As APC briefly mentioned, it only seems to be self that is required; you can use something else in place of sctx and ctx2. I can't find a documentation reference...
Edit 2 Yes, I can... APC's reference to it being an object thing led me to this.

Resources