PLS-00307 when subprograms differ only in type parameters (RAW vs VARCHAR2) - oracle

I have a package with two methods:
create or replace package demo
as
function overloaded(p_in varchar2)
return pls_integer;
function overloaded(p_in raw)
return pls_integer;
end;
/
create or replace package body demo
as
function overloaded(p_in raw)
return pls_integer
is
begin
return 1;
end;
function overloaded(p_in varchar2)
return pls_integer
is
begin
return 2;
end;
end;
/
It compiles without any errors, but I cannot call either method, as in both cases I get error PLS-00307: too many declarations match this call. Why does this happen with RAW and VARCHAR2? How can I work around this limitation? Is giving different names to my subprograms the only way out?

The documentation says:
PL/SQL lets you overload nested subprograms, package subprograms, and type methods. You can use the same name for several different subprograms if their formal parameters differ in name, number, order, or data type family.
And according to the the appendix that refers to, 'varchar2 and raw are both members of the char data type family.
You can give the subprograms different names, but you can also change the name and order of the formal parameters; in this case as there is only one parameter that means you can change only the name(s):
create or replace package demo
as
function overloaded(p_in_vc varchar2)
return pls_integer;
function overloaded(p_in_raw raw)
return pls_integer;
end;
/
(and the same change in the body of course); and then call with named notation for the actual parameters:
select demo.overloaded(p_in_vc=>'test') from dual;
DEMO.OVERLOADED(P_IN_VC=>'TEST')
--------------------------------
2
select demo.overloaded(p_in_raw=>'AABB') from dual;
DEMO.OVERLOADED(P_IN_RAW=>'AABB')
---------------------------------
1

Related

Trying to call a Function inside a stored procedure in oracle

i am trying to call a function from stored procedure in Oracle, but not getting any idea how to do.
my function has two IN parameter and one OUT parameter.
in my procedure i am using out sys refcursor . Any refrence or example will help me a lot.
Here is a simple example for calling function inside procedure. Also as mentioned by APC using OUT in function is a bad practice. Instead you can return your required output. And I'm not sure how you are using sys_refcursor, so modify your procedure accordingly
CREATE OR REPLACE FUNCTION SUM_OF_2(NUM1 IN NUMBER,NUM2 IN NUMBER) RETURN NUMBER
IS
RESULT_SUM NUMBER;
BEGIN
RESULT_SUM:=NUM1+NUM2;
RETURN RESULT_SUM;
END;
CREATE OR REPLACE PROCEDURE CALL_FUNCTON(NUM1 NUMBER,NUM2 NUMBER)
AS
V_FINAL_RESULT NUMBER;
BEGIN
V_FINAL_RESULT:=SUM_OF_2(NUM1,NUM2);
DBMS_OUTPUT.PUT_LINE(V_FINAL_RESULT);
END;
BEGIN
CALL_FUNCTON(5,10);
END;
/
CHECK DEMO HERE
Not sure on what your requirement is , maybe you are just trying the code for education purposes. Generally I have not seen much code which uses OUT parameter with functions, in case you want to return multiple values to the caller object then you could use a procedure with more then one OUT variables. There are some limitation on how an oracle function with OUT parameter would differ from a normal function.
CREATE OR REPLACE FUNCTION temp_demo_func(out_var1 OUT NUMBER)
RETURN VARCHAR2 IS
BEGIN
out_var1 := 1;
RETURN 'T';
EXCEPTION
WHEN OTHERS THEN
RETURN 'F';
END temp_demo_func;
/
CREATE OR REPLACE PROCEDURE temp_demo_proc
(
in_var1 NUMBER
,cur_refcur_out OUT SYS_REFCURSOR
) IS
res VARCHAR2(1);
out_var1 NUMBER;
BEGIN
res := temp_demo_func(out_var1 => out_var1);
dbms_output.put_line(out_var1);
OPEN cur_refcur_out FOR
SELECT in_var1
,out_var1
,res
FROM dual;
END;
/
set serveroutput on
declare
cur_refcur_out Sys_Refcursor;
in_var1 number := 22;
begin
temp_demo_proc(in_var1 => in_var1
,cur_refcur_out => cur_refcur_out);
end;
/

The compiler ignores NOCOPY in these cases

I read about IN OUT and NOCOPY. Then I encountered NOCOPY use cases but I was not able to get it. Can anybody explain these with examples? Thanks in advance.
The actual parameter must be implicitly converted to the data type of the formal parameter.
The actual parameter is the element of a collection.
The actual parameter is a scalar variable with the NOT NULL constraint.
The actual parameter is a scalar numeric variable with a range, size, scale, or precision constraint.
The actual and formal parameters are records, one or both was declared with %ROWTYPE or %TYPE, and constraints on corresponding fields differ.
The actual and formal parameters are records, the actual parameter was declared (implicitly) as the index of a cursor FOR LOOP statement, and constraints on corresponding fields differ.
The subprogram is invoked through a database link or as an external subprogram.
The basic principle is that PL/SQL will honour the NOCOPY directive as long as the value we pass can be used as provided, without transformation, and is addressable by the called program. The scenarios you list are circumstances where this is not the cases. I must admit a couple of these examples made me think, so this is a worthwhile exercise.
The first four examples call this toy procedure.
create or replace procedure tst2 (p1 in out nocopy t34%rowtype) is
begin
p1.id := 42;
end;
/
Case 1: The actual parameter must be implicitly converted to the data type of the formal parameter.
declare
n varchar2(3) := '23';
begin
tst(n);
dbms_output.put_line(n);
end;
/
Case 2: The actual parameter is the element of a collection.
declare
nt sys.odcinumberlist := sys.odcinumberlist(17,23,69);
begin
tst(nt(2));
dbms_output.put_line(to_char(nt(2)));
end;
/
Case 3: The actual parameter is a scalar variable with the NOT NULL constraint.
declare
n number not null := 23;
begin
tst(n);
dbms_output.put_line(to_char(n));
end;
/
Case 4: The actual parameter is a scalar numeric variable with a range, size, scale, or precision constraint.
declare
n number(5,2) := 23;
begin
tst(n);
dbms_output.put_line(to_char(n));
end;
/
The next example uses this table ...
create table t34 (id number not null, col1 date not null)
/
...and toy procedure:
create or replace procedure tst2 (p1 in out nocopy t34%rowtype) is
begin
p1.id := 42;
end;
/
Case 5 : The actual and formal parameters are records, one or both was declared with %ROWTYPE or %TYPE, and constraints on corresponding fields differ.
declare
type r34 is record (id number, dt date);
r r34;
begin
r.id := 23;
r.dt := to_date(null); --trunc(sysdate);
tst2(r);
dbms_output.put_line(to_char(r.id));
end;
/
The next example uses this package spec...
create or replace package pkg is
type r34 is record (id number, dt date);
end;
/
...and toy procedure:
create or replace procedure tst3 (p1 in out nocopy pkg.r34) is
begin
p1.id := p1.id + 10;
end;
/
Case 6: The actual and formal parameters are records, the actual parameter was declared (implicitly) as the index of a cursor FOR LOOP statement, and constraints on corresponding fields differ.
begin
for j in ( select * from t34) loop
tst3(j);
dbms_output.put_line(to_char(j.id));
end loop;
end;
/
The last example uses a remote version of the first procedure.
Case 7: The subprogram is invoked through a database link or as an external subprogram.
declare
n number := 23;
begin
tst#remote_db(n);
dbms_output.put_line(to_char(n));
end;
/
There are working demos of the first six cases on 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;
/

too many declarations of

I am getting the following error message while running the below code. I am new to coding world of pl/sql (oracle) and I request your assistance for the same.
Code:
create or replace package learn is
function Area(i_rad NUMBER) return NUMBER;
function Area(i_length NUMBER, i_width NUMBER:=3) return NUMBER;
end;
/
Package body:
create or replace package body learn is
function Area(i_rad NUMBER) return NUMBER
is
v_pi NUMBER:=3.14;
v number:=to_number(i_rad);
begin
return v_pi * (i_rad ** 2);
end;
function Area(i_length NUMBER, i_width NUMBER:=3) return NUMBER
is
begin
return i_length * i_width;
end;
end learn;
Plsql block
declare
x number(2):=2;
y number(2):=5;
begin
DBMS_OUTPUT.put_line('Area (R=3):'||learn.Area(x));
DBMS_OUTPUT.put_line('Area (R=3):'||learn.Area(x,y));
end;
Error Message: too many declarations of 'AREA' match this call
That's because you have default value for the second parameter in your two param function. If you provide only param, the second function will assume the second value to be 3 and now there are two functions that can be called and hence the call failed.
I'd suggest you not to do this kind of overloading as it is not clear which function does what.
If you still want to do this, one way is to make the second param mandatory and pass null if you don't have any value to pass.
create or replace package learn is
function Area(i_rad NUMBER) return NUMBER;
function Area(i_length NUMBER, i_width NUMBER) return NUMBER;
end;
/
create or replace package body learn is
function Area(i_rad NUMBER) return NUMBER
is
v_pi NUMBER:=3.14;
v number:=to_number(i_rad);
begin
return v_pi * (i_rad ** 2);
end;
function Area(i_length NUMBER, i_width NUMBER) return NUMBER
is
begin
return i_length * nvl(i_width,3);
end;
end learn;
/
declare
x number(2):=2;
y number(2):=5;
begin
DBMS_OUTPUT.put_line('Area (R=3):'||learn.Area(x));
DBMS_OUTPUT.put_line('Area (R=3):'||learn.Area(x,y));
end;
/
If you have different param names, you can do this:
declare
x number(2):=2;
y number(2):=5;
begin
DBMS_OUTPUT.put_line('Area (R=3):'||learn.Area(i_rad => x));
DBMS_OUTPUT.put_line('Area (R=3):'||learn.Area(x,y));
end;
/
Since i_width has a default value, you have two functions that can be called with a single number argument. Since both these functions calculate different areas, a good way to differentiate would be to simply use different names:
CREATE OR REPLACE PACKAGE learn IS
FUNCTION circle_area(i_rad NUMBER) RETURN NUMBER;
FUNCTION rectangle_area(i_length NUMBER, i_width NUMBER:=3) RETURN NUMBER;
-- And the same changes in the package body, of course.
END;
/

How to use Pl/SQL Collection within a Function

For an assignment the recommendation was to use collections. Simply said I need to chop a string in several substrings, reverse the order of those strings and put them all together.
Omitted actual code in because it crashes at the declare part.
Keep getting the error: stringlist1 is not a procedure or is undefined
So for some reason the type I try to assign to stringlist1 isn't doing it.
Do you guys know why this is happening? And if this isn't fixable what is a neat workaround other than making a variable for every row in the collection?
Things I tried:
* Create my own type in a "create type" statement beforehand
* Creating the type in an anonymous block before the function
* Creating a varchar collection within the declare part of the function
* The dbms package used below
* Renaming the hell out of it
* Trying to initiate the type like stringlist1 dbms_utility.name_array := dbms_utility.name_array();
Create or replace FUNCTION TEST (p_number varchar2)
RETURN varchar2
IS
stringlist1 dbms_utility.name_array;
stringlist2 dbms_utility.name_array;
BEGIN
stringlist1('test');
stringlist2('test');
dbms.output.put_line(stringlist1(1));
return stringlist1(1);
END;
String can be added to array like stringlist1(1) := 'test';
So your code will be compile when corrected like
CREATE OR REPLACE FUNCTION TEST (p_number VARCHAR2)
RETURN VARCHAR2
IS
stringlist1 DBMS_UTILITY.name_array;
stringlist2 DBMS_UTILITY.name_array;
BEGIN
stringlist1(1) := 'test';
stringlist2(1) := 'test';
dbms_output.put_line (stringlist1 (1));
RETURN stringlist1 (1);
END;
PS: Package name is not dmbs.output, correct one is dbms_output.

Resources