too many declaration of a construct match this call - oracle

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

Related

How to solve the error "PLS-00363: expression cannot be used as an assignment target" in 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

difference between creating a type with new keyword or without in plsql

In oracle pl/sql you can create an object with or without the new keyword.
Is there any difference how oracle threats these requests ?
So for example:
CREATE TYPE emp_object AS OBJECT(
emp_no NUMBER,
emp_name VARCHAR2(50),
salary NUMBER,
manager NUMBER,
CONSTRUCTOR FUNCTION emp_object(p_emp_no NUMBER, p_emp_name VARCHAR2,
p_salary NUMBER) RETURN SELF AS RESULT),
MEMBER PROCEDURE insert_records,
MEMBER PROCEDURE display_records);
/
CREATE OR REPLACE TYPE BODY emp_object AS
CONSTRUCTOR FUNCTION emp_object(p_emp_no NUMBER,p_emp_name VARCHAR2,
p_salary NUMBER)
RETURN SELF AS RESULT
IS
BEGIN
Dbms_output.put_line('Constructor fired..');
SELF.emp_no:=p_emp_no;|
SELF.emp_name:=p_emp_name;
SELF.salary:=p_salary;
SELF.managerial:=1001;
RETURN;
END:
MEMBER PROCEDURE insert_records
IS
BEGIN
INSERT INTO emp VALUES(emp_noemp_name,salary,manager);
END
MEMBER PROCEDURE display_records
IS
BEGIN
Dbms_output.put_line('Employee Name:'||emp_name);
Dbms_output.put_line('Employee Number:'||emp_no);
Dbms_output.put_line('Salary':'||salary);
Dbms_output.put_line('Manager:'||manager);
END:
END:
/
This is a type with spec and body, now you can create the object like this without the new keyword:
DECLARE
guru_emp_det emp_object;
BEGIN
guru_emp_det:=emp_object(1005,'RRR',20000,1000);
guru_emp_det.display_records;
guru_emp_det.insert_records;
COMMIT;
END;
But it is also possible with the keyword:
DECLARE
guru_emp_det emp_object;
BEGIN
guru_emp_det:= new emp_object(1005,'RRR',20000,1000);
guru_emp_det.display_records;
guru_emp_det.insert_records;
COMMIT;
END;
From the Type Constructor Expressions documentation:
Type Constructor Expressions
A type constructor expression specifies a call to a constructor method. The argument to the type constructor is any expression. Type constructors can be invoked anywhere functions are invoked.
type_constructor_expression::=
The NEW keyword applies to constructors for object types but not for collection types. It instructs Oracle to construct a new object by invoking an appropriate constructor. The use of the NEW keyword is optional, but it is good practice to specify it.
As to your question:
Is there any difference how oracle treats these requests?
No, there is no difference; the NEW keyword is optional.
Although the documentation states that the NEW keyword does not apply for collection types, in practice it does not appear to be invalid syntax and is both allowable and optional for collection types (there may be an edge-case I have not yet found where the NEW keyword is forbidden; however, on some brief tests I cannot find such a case).
fiddle

Constants in object

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

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

Oracle, EXTENDS mus be declared

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;

Resources