Automatically execute code on package startup - oracle

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

Related

Flyway parser fails while trying to create oracle packages from .sql script

I have got an SQL script stored in an .sql-file containing several statements like this:
create or replace package <schema>.custom_package is
procedure getReleaseEmployees(p_Cursor in out sys_refcursor, p_Role in string);
end custom_package;
/
create or replace package <schema>.pck_account_monitoring is
procedure checkAndLockOracleAccount;
end pck_account_monitoring;
/
<...some more packages following>
Running the script in an SqlDeveloper or PL/SQLDeveloper against my database works fine.
Now, when I'm trying to migrate via Flyway using the Java API, I'll get the following error message and Flyway's migration fails:
SQL State : 42000
Error Code : 900
Message : ORA-00900: Invalid SQL Statement
Location : migrations/sql/V0_2__migration.sql
Line : 6172
Statement : end pck_account_monitoring
at org.flywaydb.core.internal.dbsupport.SqlScript.execute(SqlScript.java:117)
at org.flywaydb.core.internal.resolver.sql.SqlMigrationExecutor.execute(SqlMigrationExecutor.java:71)
at org.flywaydb.core.internal.command.DbMigrate.doMigrate(DbMigrate.java:352)
If I try to put the first statement into a single line, i.e.
create or replace package quattro.custom_package is procedure getReleaseEmployees(p_Cursor in out sys_refcursor, p_Role in string); end custom_package;
/
<rest as before>
I'll get the same error message, but the parser now complains about the '/' symbol and the beginning of the next statement:
Error Code : 900
Message : ORA-00900: Invalid SQL Statement
...
Line : 6167
Statement : /
create or replace package quattro.pck_account_monitoring is ...
Now, if I remove that first '/' as well, the migration will run (with warnings, though).
My schema will then correctly have compiled all the other packages except for the first one - even though every other package declaration is formatted like the first one was in my initial attempt! The first package (custom_package) however is still missing.
My Java class basically looks like the sample class from the Flyway homepage.
Does anyone have any idea what's going wrong here with the Flyway parser or can maybe provide me with a workaround?
(This is too long for a comment, therefore, I'm posting it as an answer)
FWIW, I cannot reproduce this (using the Flyway 3.2.1 command line tool); running this example script:
-- PACKAGES
create or replace package flyway_demo.custom_package is
procedure getReleaseEmployees(p_Cursor in out sys_refcursor, p_Role in string);
end custom_package;
/
create or replace package flyway_demo.pck_account_monitoring is
procedure checkAndLockOracleAccount;
end pck_account_monitoring;
/
create or replace package flyway_demo.logger
authid definer
as
-- TYPES
type rec_param is record(
name varchar2(255),
val varchar2(4000));
type tab_param is table of rec_param index by binary_integer;
g_off constant number := 0;
function is_number(p_str in varchar2)
return boolean;
end logger;
/
create or replace package body flyway_demo.pck_account_monitoring is
procedure checkAndLockOracleAccount is
begin
null;
end;
end pck_account_monitoring;
/
create or replace package body flyway_demo.custom_package is
procedure getReleaseEmployees(p_Cursor in out sys_refcursor, p_Role in string)
is
begin
open p_Cursor for select * from dual;
end;
end custom_package;
/
create or replace package body flyway_demo.logger
as
function is_number(p_str in varchar2) return boolean is
begin
return true;
end;
end logger;
/
works perfectly fine. Can you please try it with this script and post your findings?

Initialization section of the package

This is the package specification:
CREATE OR REPLACE PACKAGE employee_info
IS
PROCEDURE p;
FUNCTION f RETURN BOOLEAN;
END employee_info;
This is the package body:
CREATE OR REPLACE PACKAGE body employee_info
IS
PROCEDURE p IS
BEGIN
dbms_output.put_line('This is procedure');
dbms_output.put_line(chr(10));
END;
FUNCTION f RETURN BOOLEAN IS
BEGIN
dbms_output.put_line('This is function ');
dbms_output.put_line(chr(10));
RETURN true;
END;
BEGIN
dbms_output.put_line('This is the initialization section of package body');
END employee_info;
When should I use this initialization section of the package?
As in the above example, when I first execute the code then only i can see that begin..end of package body is executed and for rest of call it is not executed.
I am executing the procedure and function using below statements:
execute employee_info.p;
declare
p1 boolean;
begin
p1 := employee_info.f;
end;
Package initialization section as the name suggest is executed when package is initialized. This happens when first procedure/function from the package is executed after session is established or after package is (re)compiled. The purpose is to initialize the global state of the package that can be used during session lifetime. All package global variables are kept and you can access them later.
Example:
HUSQVIK#hq_pdb_tcp> CREATE OR REPLACE PACKAGE test_package
2 IS
3 PROCEDURE foo;
4 END;
5 /
Package created.
HUSQVIK#hq_pdb_tcp> CREATE OR REPLACE PACKAGE BODY test_package
2 IS
3 PROCEDURE foo
4 IS
5 BEGIN
6 DBMS_OUTPUT.PUT_LINE('Procedure executed. ');
7 END;
8
9 BEGIN
10 DBMS_OUTPUT.PUT_LINE('Package initialized. ');
11 END;
12 /
Package body created.
HUSQVIK#hq_pdb_tcp> EXEC test_package.foo
Package initialized.
Procedure executed.
PL/SQL procedure successfully completed.
HUSQVIK#hq_pdb_tcp> EXEC test_package.foo
Procedure executed.
PL/SQL procedure successfully completed.
HUSQVIK#hq_pdb_tcp>
You see that after package is compiled the initialization section is executed when procedure foo is executed. The package is initialized now. Any subsequent execution of foo executes only the procedure.

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.

PL/SQL call a function outside a package

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;

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