PL/SQL call a function outside a package - oracle

I define a function outside a package, tried to call this function, failed.
how to fix it ? thanks
create or replace
package body test_erp AS
procedure init_data is
begin
logMessage('procedure init_data');
end init_data;
end test_erp;
/
show error
error is
PLS-00221: 'LOGMESSAGE' is not a procedure or is undefined

As the error suggests logmessage is not a procedure. It's a function. As functions return something you need to assign this to a variable. You know that logmessage returns a number so you need to declare a variable to put this return value into.
create or replace package body test_erp AS
procedure init_data is
l_success number;
begin
l_message := logMessage('procedure init_data');
dbms_output.put_line(to_char(l_success));
end init_data;
end test_erp;
/
However, it looks like logmessage should in fact be a procedure. I assume you're executing DML statements (update/insert) in this. A function call be used in a select statement unless this is the case, which means that there's always the possibility of an error occurring. If logmessage were a procedure you can declare an out parameter to tell the calling procedure whether everything worked or not; something like the following:
create or replace procedure logmessage( msg in varchar2, success out number) is
begin
insert into logs values(msg);
success := 1;
exception when others then
success := 0;
end logmessage;
You can then call it as follows:
create or replace package body test_erp AS
procedure init_data is
l_success number;
begin
logMessage('procedure init_data', l_success);
dbms_output.put_line(to_char(l_success));
end init_data;
end test_erp;
/
If logmessage isn't going to be used outside the package test_erp I would put it inside the package; it keeps the namespace cleaner and avoids it getting used mistakenly be another package / call etc.

Assuming that logMessage is the same function from this post:
Since logMessage is a function (and returns a number) you need to call it like this:
procedure init_data is
i number;
begin
i := logMessage('procedure init_data');
end init_data;

Related

Best way to manage exception in package

I have a package with a several procedures and functions, the procedures are called from an external program and in turn they call functions.
Where is the best way to manage a Exception?
For example, Prog1 call Proc1 and Proc1 call the Funct1, if in function I have an exception ("TOO MANY ROW" or "NO DATA FOUND"), which is the best way to raise a specific customized message and return immediatally to Prog1.
In this moment I have this
PROCEDURE_1
BEGIN
*code*
CALL FUNCTION_1
*code*
EXCEPTION
WHEN NO DATA FOUND THEN
*print customized message*
RETURN;
END;
FUNCTION_1
BEGIN
*code*
EXCEPTION
WHEN NO DATA FOUND THEN
RAISE;
END;
It is the best way?
Regards,
Marco
Ideally a procedure would be allowed to fail with an exception and error stack if something went totally wrong, but then the difference between a fatal error and an expected condition will vary depending on the business logic you are implementing, so it's hard to say what should happen in your particular case.
If the agreed interface is that the procedure should return a formatted message, and that message may include expected business conditions such as a product being out of stock, then you can handle that within the procedure using something like this (nonsense pseudocode to illustrate approach only):
create or replace procedure procedure_1
( p_result_message out varchar2 )
as
somevar number;
begin
do_stuff('fruit','cake');
begin
somevar := function_1('bananas');
exception
when no_data_found then
p_result_message := 'No kittens are available for this mission.';
return;
end;
p_result_message := 'Cake is available in aisle 3';
exception
when something_else then
p_result_message := 'Self-destruct sequence initiated.';
end;
(For a more purist approach you might prefer to rearrange the code so that it always reaches the end with a value for p_result_message, rather than quitting partway through if some condition pops up.)
Now you have a way to handle whatever exceptions might reasonably arise in function_1, without any special handling within the function itself.
You could also have the function raise an exception defined in a package, although then you lose the ability to define a diagnostic error message at the point of failure, and in my experience this just tends to complicate things. But to illustrate:
create or replace package starfleet
as
shield_failure exception;
warp_core_malfunction exception;
pragma exception_init(shield_failure, -20998);
pragma exception_init(warp_core_malfunction, -20999);
procedure check_status
( status_message out varchar2 );
function status
return number;
end starfleet;
create or replace package body starfleet as
function status
return number
is
status_ind number;
begin
select 1 into status_ind from dual where 1=2; -- fails with NDF
return status_ind;
exception
when no_data_found then raise shield_failure;
end status;
procedure check_status
( status_message out varchar2 )
is
status_ind number;
begin
status_ind := status();
status_message := 'Everything is fine';
exception
when warp_core_malfunction then status_message := 'Abandon ship';
when shield_failure then status_message := 'Increase power to shields';
end check_status;
end starfleet;
Now, the status() function can raise exceptions defined in the package (or any other package for that matter), and procedure check_status can catch it and decide how to handle it.
Example of calling it from SQL*Plus:
SQL> var status_message varchar2(100)
SQL>
SQL> begin
2 starfleet.check_status(:status_message);
3 end;
4 /
PL/SQL procedure successfully completed.
STATUS_MESSAGE
-------------------------
Increase power to shields

What does it means when there are two functions with the same name but different parameter in the package body?

In the package spec, the function name only appears once therefore it's not overloaded. In the package body, the same function name appears twice using different set of parameters. One of them has the same parameter as the one in the package spec. My question is, is the "first func" in the package body actually being invoked and if so, what exactly is it doing? It's trying to return itself.
--package specification
CREATE OR REPLACE
PACKAGE jtestpkg
IS
FUNCTION testfunc(p_num IN NUMBER, p_out1 IN out varchar2, p_out2 IN out varchar2)
RETURN NUMBER;
END jtestpkg;
--package body
CREATE OR REPLACE
PACKAGE BODY jtestpkg
IS
--first func
function testfunc
(p_num IN NUMBER,
p_out1 IN OUT varchar2)
return number is
v_out2 varchar2(50);
BEGIN
dbms_output.put_line('invoking first func');
RETURN testfunc(
p_num,
p_out1,
v_out2);
END;
--second func
FUNCTION testfunc(
p_num IN NUMBER,
p_out1 IN OUT varchar2,
p_out2 IN OUT varchar2)
RETURN NUMBER
IS
v_num number;
BEGIN
IF 1=p_num THEN
p_out1:='FirstOUT_1';
p_out2:='SecondOUT_1';
dbms_output.put_line(v_num||p_out1||p_out2);
RETURN 1;
elsif 2=p_num THEN
p_out1:='FirstOUT_2';
p_out2:='SecondOUT_2';
dbms_output.put_line(v_num||p_out1||p_out2);
RETURN 2;
ELSE
p_out1:='FirstOUT_3';
p_out2:='SecondOUT_3';
dbms_output.put_line(v_num||p_out1||p_out2);
return 3;
END IF;
------
p_out1:='FirstOUT_0';
p_out2:='SecondOUT_0';
dbms_output.put_line(v_num||p_out1||p_out2);
RETURN 0;
END testfunc;
END jtestpkg;
A function declared in the specification is public and can be invoked from outside the package. A function which is defined in the body but not declared in the specification is private and can only be called from within that package.
In your example the second overloaded version of your function in the package body, which you've labelled 'second func', matches the declaration in the specification, so that is the one that is involved when you call the function from elsewhere:
declare
rc number;
in_out_1 varchar2(20) := 'A';
in_out_2 varchar2(20) := 'B';
begin
rc := jtestpkg.testfunc(42, in_out_1, in_out_2);
end;
/
FirstOUT_3SecondOUT_3
PL/SQL procedure successfully completed.
The first overloaded function in your body, which you've labelled 'first func', does not have a matching declaration in the specification, so you can't call it externally:
declare
rc number;
in_out_1 varchar2(20) := 'A';
begin
rc := jtestpkg.testfunc(42, in_out_1);
end;
/
ORA-06550: line 5, column 9:
PLS-00306: wrong number or types of arguments in call to 'TESTFUNC'
ORA-06550: line 5, column 3:
PL/SQL: Statement ignored
My question is, is the "first func" in the package body actually being invoked
No. In your code the 'first func' is not ever being invoked.
It's trying to return itself.
No it isn't. Your 'first func' would call 'second func' if it was itself called from somewhere else inside the package, but you aren't currently doing that.
The debug in there says 'invoking first func' but that isn't true, it's invoking second func, since the call it makes has three arguments - matching the 'second func' argument list. (That happens to be public, but it wouldn't matter if it wasn't, since it's internal to the package anyway).
Just as an example, you could call the private function as part of the package instantiation and initialization:
...
END testfunc;
-- initialization, called on instantiation (for each session)
BEGIN
dbms_output.put_line('Initialization start');
declare
rc number;
in_out_1 varchar2(20) := 'A';
begin
dbms_output.put_line('Initialization: calling first func');
rc := testfunc(1, in_out_1);
end;
dbms_output.put_line('Initialization end');
END jtestpkg;
/
then calling anything public in the package the first time in a session instantiates it, which initializes it, which runs that package-level block. So with the same anonymous block:
declare
rc number;
in_out_1 varchar2(20) := 'A';
in_out_2 varchar2(20) := 'B';
begin
rc := jtestpkg.testfunc(42, in_out_1, in_out_2);
end;
/
you see (first time in the session only):
Initialization start
Initialization: calling first func
invoking first func
FirstOUT_1SecondOUT_1
Initialization end
FirstOUT_3SecondOUT_3
PL/SQL procedure successfully completed.
You still see the same FirstOUT_3SecondOUT_3 output as before, from the value 42 passed in that call; but before that you see the output FirstOUT_1SecondOUT_1 from 'first func' calling 'second func' with value 1 as part of that initialization process.
It is allowed for a function to call itself, i.e. recursively, but it would need to change the call each time or it would get stuck in an infinite loop, and eventually be killed. You aren't doing that either here though.

Automatically execute code on package startup

I have an exam coming up and have a specification which I'm not sure what it means. Can please give me someone a hint or give me a short example for it.
Specification: Automatically execute code on package Startup:
If criteria met: Code written that automatically initialises variable
without explicit call from user.
I'm not sure if this is asked but can it be that I have to call the package like this: PACKAGE_NAME.function_name so a call without a value? Is that what is asked?
If so, you don't need to help me further because I know what to do .
You can have code in the package body that is not part of any of the private or public functions or procedures; that code is executed when the package is instantiated by each session.
As a contrived example, you might want to populate a variable with the initialisation time, which you could do with:
create package p42 as
procedure some_proc;
end p42;
/
create package body p42 as
init_time timestamp;
procedure some_proc is
begin
-- do something
null;
end some_proc;
-- run on package instantiation
begin
init_time := systimestamp;
-- just as a demo
dbms_output.put_line('init_time is ' || init_time);
end p42;
/
That last block is run when the package is instantiated for each session, i.e. when the first procedure or function is called by that session, and not when it is compiled:
Package body P42 compiled
set serveroutput on
exec p42.some_proc;
PL/SQL procedure successfully completed.
init_time is 19-MAY-15 15.41.29.179387
... and not when other procedures are called from the same session:
exec p42.some_proc;
PL/SQL procedure successfully completed.
That variable could then be referenced later. Of course having that variable means you now have package state, which causes its own problems. But you could just call other procedures during initialisation instead.
Read more in the documenation; about instantiation, state, and the initialisation part of the package body:
Finally, the body can have an initialization part, whose statements initialize public variables and do other one-time setup steps. The initialization part runs only the first time the package is referenced. The initialization part can include an exception handler.
You can do it like this:
CREATE OR REPLACE PACKAGE MY_PACKAGE AS
FUNCTION GiveMeFive RETURN NUMBER;
END MY_PACKAGE;
/
CREATE OR REPLACE PACKAGE BODY MY_PACKAGE IS
five NUMBER;
FUNCTION GiveMeFive RETURN NUMBER IS
BEGIN
RETURN five;
END GiveMeFive;
-- By this syntax the procedure is running once when this package is executed first time within a session
BEGIN
five := 5;
END;
/
Note, the package body has to close just by /, do not use
BEGIN
five := 5;
END;
END MY_PACKAGE;
/

How to call an Oracle procedure within a package?

I'm trying to call an Oracle stored procedure within a package and I'm getting this error:
SQL Error: ORA-06576: not a valid function or procedure name
I'm using SQL Developer and this is the command I'm using
call WEATHERDATAUPDATES.GetLastRunDate("WeatherData")
Here is the package/procedure
PACKAGE BODY WEATHERDATAUPDATES AS
PROCEDURE GetLastRunDate(PROCESS IN VARCHAR2, RUNDATE OUT DATE) AS
BEGIN
SELECT rundate FROM Marcie.last_rundate
where process = PROCESS;
END GetLastRunDate;
END WEATHERDATAUPDATES;
I'm pretty new to Oracle Packages and not sure what I'm missing. I tried searching, but can't find an answer that works. Can someone tell me what I'm missing?
Thanks,
Marcie
In your procedure you are not putting the retrieved value anywhere, you should use INTO:
...
PROCEDURE GetLastRunDate(PROCESS IN VARCHAR2, RUNDATE OUT DATE) AS
BEGIN
SELECT rundate
INTO RUNDATE
FROM Marcie.last_rundate
where process = PROCESS;
END GetLastRunDate;
...
In the call pass the variable for the out parameter RUNDATE OUT DATE.
Put the call in a PL/SQL block:
DECLARE
lastRunDate DATE;
BEGIN
WEATHERDATAUPDATES.GetLastRunDate("WeatherData",lastRunDate);
-- do something with lastRunDate
END;
GetLastRunDate has 2 parameters, (process and rundate), but you're only passing 1 in your call.
Since you want to pass the second parameter out, one option would be to make it function and return the rundate.
PACKAGE BODY WEATHERDATAUPDATES AS
FUNCTION GetLastRunDate(PROCESS IN VARCHAR2)
RETURN DATE
AS
lDate DATE;
BEGIN
SELECT rundate
INTO lDate
FROM Marcie.last_rundate
where process = PROCESS;
RETURN lDate;
END GetLastRunDate;
END WEATHERDATAUPDATES;
You have shown the BODY of your package, but not the specification - check that GetLastRunDate is declared in the package spec.

Find out name of PL/SQL procedure

Can PL/SQL procedure in Oracle know it's own name?
Let me explain:
CREATE OR REPLACE procedure some_procedure is
v_procedure_name varchar2(32);
begin
v_procedure_name := %%something%%;
end;
After %%something%% executes, variable v_procedure_name should contain 'SOME_PROCEDURE'. It is also OK if it contains object_id of that procedure, so I can look up name in all_objects.
Try:
v_procedure_name := $$PLSQL_UNIT;
There's also $$PLSQL_LINE if you want to know which line number you are on.
If you are pre-10g, you can 'dig' (parse) it out of
dbms_utility.format_call_stack
Procedures/functions in packages can be overloaded (and nested), so the package name/line number is normally better than the name.
In 10g and 11g I use the "owa_util.get_procedure" function. I normally use this in packages as it will also return the name of an internal procedure or function as part of the package name, i.e. (package_name).(procedure name). I use this to provide a generic EXCEPTION template for identifying where an exception occured.
CREATE OR REPLACE procedure some_procedure is
v_procedure_name varchar2(32);
begin
v_procedure_name := owa_util.get_procedure;
end;
CREATE OR REPLACE PACKAGE some_package
AS
FUNCTION v_function_name
RETURN DATE;
END;
/
CREATE OR REPLACE PACKAGE BODY some_package
AS
FUNCTION v_function_name
RETURN DATE
IS
BEGIN
RETURN SYSDATE;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ERROR IN '||owa_util.get_procedure);
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
END;
/
Here's a neat function that takes advantage of REGEXP_SUBSTR.
I've tested it in a package (and it even works if another procedure in the package calls it):
FUNCTION SET_PROC RETURN VARCHAR2 IS
BEGIN
RETURN NVL(REGEXP_SUBSTR(DBMS_UTILITY.FORMAT_CALL_STACK,
'procedure.+\.(.+)\s', 1,1,'i',1), 'UNDEFINED');
END SET_PROC;

Resources