Procedure in package specification - oracle

I have a package named save_db_values
I have two procedures named store_records and another one called db_activities.
db_activities will be called from my application by passing all values in db_activities I will be calling store_records procedure to do insert and delete.
Do I need to define store_records procedure in package specification? When I did not define store_records in specification I am getting error store_records not declared in this scope.
store_records procedure I do not want to expose and hence I did not add in specification.
How can I resolve this issue?

If you do not want some procedures to be publicly available you may not to declare them in the package specification. Declare them only in the package body. The cause of the error you are facing is declaration order of the procedures in the package body or lack of forward declaration. For example:
create or replace package Test_pkg as
2 procedure Proc1;
3 end;
4 /
Package created
create or replace package body Test_pkg as
2
3 procedure proc1 is
4 begin
5 proc2;
6 end;
7
8 procedure Proc2 is
9 begin
10 dbms_output.put_line('proc2 is being executed');
11 end;
12
13 end;
14 /
Warning: Package body created with compilation errors
Error: PLS-00313: 'PROC2' not declared in this scope
This is happening because we are calling Proc2 which declared later in the package. In this case our choices are:
Declare pro2 before the procedure which calls it
create or replace package body Test_pkg as
2
3
4 procedure Proc2 is
5 begin
6 dbms_output.put_line('proc2 is being executed');
7 end;
8
9 procedure proc1 is
10 begin
11 proc2;
12 end;
13
14 end;
15 /
Package body created
Use forward declaration.
create or replace package body Test_pkg as
2
3 procedure Proc2;
4
5 procedure proc1 is
6 begin
7 proc2;
8 end;
9
10 procedure Proc2 is
11 begin
12 dbms_output.put_line('proc2 is being executed');
13 end;
14
15
16 end;
17 /
Package body created
SQL> exec test_pkg.Proc1;
proc2 is being executed
PL/SQL procedure successfully completed

You can declare procedures just in the body, but the order in which they appear is relevant; the calling procedure must be defined after the called procedure. Or you use a forward declaration to make it easier:
package save_db_values is
procedure db_activities;
end save_db_values;
package body save_db_values is
procedure store records; -- forward declaration
procedure db_activities is
begin
store_records;
end;
procedure store records is
begin
null;
end;
end save_db_values;

It is happening because of writing procedure's body in the package body .
if you are not declaring any procedure in the package specification then you should write it at first place.
it will work :)

Related

PLSQL Subtype of Nested Table PLS-00355: use of pl/sql table not allowed in this context

Edit: as per the answers, this works in 21C but not before. Looks like it is an Oracle bug that was fixed. The workaround in previous versions is provided as the accepted answer.
In PL/SQL, a subtype of a nested table behaves inconsistently compared to a regular nested table. It seems like it is not possible to use a subtype of a nested table. Is this correct?
create or replace package test_subtype_pkg
is
type t_foos is table of varchar2(10);
subtype t_bars is t_foos;
procedure main;
end;
/
The following l_bars := t_bars() fails to compile with PLS-00355: use of pl/sql table not allowed in this context
create or replace package body test_subtype_pkg
is
procedure main
is
l_foos t_foos;
l_bars t_bars;
begin
l_foos := t_foos(); -- compiles correctly
l_bars := t_bars(); -- PLS-00355: use of pl/sql table not allowed in this context
end;
end;
/
However, the following compiles without error. During run time it fails with ORA-06531: Reference to uninitialized collection:
create or replace package body test_subtype_pkg
is
procedure main
is
l_bars t_bars;
begin
l_bars.extend; -- At run time, ORA-06531: Reference to uninitialized collection
l_bars(1) := 'foo';
end;
end;
/
Is it possible to use a subtype of a nested table?
You can use the sub-type but it only appears to work if you call the constructor from the base-type:
create or replace package test_subtype_pkg
is
type t_foos is table of varchar2(10);
subtype t_bars is t_foos;
procedure main;
end;
/
create or replace package body test_subtype_pkg
is
procedure main
is
l_foos t_foos;
l_bars t_bars;
begin
l_foos := new t_foos();
l_bars := new t_foos();
l_bars.extend;
l_bars(1) := 'foo';
end;
end;
/
Note: the NEW keyword is optional.
Compiles without errors and then:
BEGIN
test_subtype_pkg.main();
END;
/
Works without a runtime error.
Note: You get the runtime error in your second example because you do not initialise the collection as you never call a constructor.
Oracle 18 fiddle
I don't have 18c; this is 21cXE and both codes work OK.
SQL> select banner from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production
SQL> create or replace package test_subtype_pkg
2 is
3 type t_foos is table of varchar2(10);
4 subtype t_bars is t_foos;
5
6 procedure main;
7 end;
8 /
Package created.
You said that this failed on your database; it works for me:
SQL> create or replace package body test_subtype_pkg
2 is
3 procedure main
4 is
5 l_foos t_foos;
6 l_bars t_bars;
7 begin
8 l_foos := t_foos(); -- compiles correctly
9 l_bars := t_bars(); -- PLS-00355: use of pl/sql table not allowed in this context
10 end;
11 end;
12 /
Package body created.
SQL> exec test_subtype_pkg.main;
PL/SQL procedure successfully completed.
SQL>
Your code that compiles, but doesn't work - it is because you really didn't initialize the collection (see line #5 which shows how to do it):
SQL> create or replace package body test_subtype_pkg
2 is
3 procedure main
4 is
5 l_bars t_bars := t_bars();
6 begin
7 l_bars.extend; -- At run time, ORA-06531: Reference to uninitialized collection
8 l_bars(1) := 'foo';
9 end;
10 end;
11 /
Package body created.
SQL> exec test_subtype_pkg.main;
PL/SQL procedure successfully completed.
SQL>

Calling Procedure or function in the package

I have Created a Package and procedure and functions within.
I have a small doubt.
If I have created procedure name employee_dtls in the package and at the same time
employee_dtls procedure as an independent object in the schema.
But i want to call the independent procedure employee_dtls inside my package but when I am trying to call
the procedure within the package is getting executed an independent one is not being called.
I assume since the name of both the procedures are the same it is calling the procedure inside the package.
Please help what can I do in this case?
What can you do? Qualify procedure name (as Justin commented). Here's how (read comments within code):
Standalone procedure:
SQL> create or replace procedure p_test is
2 begin
3 dbms_output.put_line('...standalone P_TEST procedure');
4 end;
5 /
Procedure created.
Packaged procedure:
SQL> create or replace package pkg_test is
2 procedure p_test;
3 procedure p_run;
4 end;
5 /
Package created.
SQL> create or replace package body pkg_test is
2 procedure p_test is
3 begin
4 dbms_output.put_line('...packaged P_TEST procedure');
5 end;
6
7
8 procedure p_run is
9 begin
10 dbms_output.put_line('calling P_TEST');
11 p_test; -- no qualifier - runs the packaged procedure (that's what you have now)
12
13 dbms_output.put_line('calling SCOTT.P_TEST');
14 scott.p_test; -- procedure name preceded by owner name - runs the standalone procedure
15
16 dbms_output.put_line('calling PKG_TEST.P_TEST');
17 pkg_test.p_test; -- procedur ename preceded by package name - runs the packaged procedure
18 end;
19 end;
20 /
Package body created.
Testing:
SQL> set serveroutput on;
SQL> exec pkg_test.p_run;
calling P_TEST
...packaged P_TEST procedure
calling SCOTT.P_TEST
...standalone P_TEST procedure
calling PKG_TEST.P_TEST
...packaged P_TEST procedure
PL/SQL procedure successfully completed.
SQL>

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 can I get the procedure/function name where an exception occurs, in PLSQL?

How can I get a procedure/function name where an exception occurs in PLSQL?
I need this to create a log of the procedures that have problems in a package.
Unfortunately there is no way to get a stored procedure name, that is wrapped in a package, at run time. Any method, whether it $$PLSQL_UNIT inquiry directive or FORMAT_ERROR_BACKTRACE function of dbms_utility package, that allows you to do that in a case of a stand-alone stored procedure will always give you the anonymous block instead of a name in a case of a stored procedure that is wrapped in the package. Hence your only choice is to capture a package name and the line number on which an error has occurred by using dbms_utility.format_error_backtrace for example.
SQL> create or replace package some_pkg
2 as
3 procedure some_proc;
4 end;
5 /
Package created
SQL>
SQL> create or replace package body some_pkg
2 as
3 procedure Some_Proc
4 is
5 l_var number;
6 begin
7 l_var := 1/0;
8 exception
9 when others
10 then dbms_output.put_line(dbms_utility.format_error_backtrace);
11 end;
12 end;
13 /
Package body created
SQL> exec some_pkg.some_proc;
ORA-06512: at "HR.SOME_PKG", line 7
Did u mean get all procedures that has 'exception' in their source ?
if so then :
select distinct t.name from user_source t
where upper(t.text) like '%EXCEPTION%' or
upper(t.text) like '%RAISE_APPLICATION_ERROR%';
I hope it helps you :)

Passing an array of data as an input parameter to an Oracle procedure

I'm trying to pass an array of (varchar) data into an Oracle procedure. The Oracle procedure would be either called from SQL*Plus or from another PL/SQL procedure like so:
BEGIN
pr_perform_task('1','2','3','4');
END;
pr_perform_task will read each of the input parameters and perform the tasks.
I'm not sure as to how I can achieve this. My first thought was to use an input parameter of type varray but I'm getting Error: PLS-00201: identifier 'VARRAY' must be declared error, when the procedure definiton looks like this:
CREATE OR REPLACE PROCEDURE PR_DELETE_RECORD_VARRAY(P_ID VARRAY) IS
To summarize, how can I pass the data as an array, let the SP loop through each of the parameters and perform the task ?
I'm using Oracle 10gR2 as my database.
This is one way to do it:
SQL> set serveroutput on
SQL> CREATE OR REPLACE TYPE MyType AS VARRAY(200) OF VARCHAR2(50);
2 /
Type created
SQL> CREATE OR REPLACE PROCEDURE testing (t_in MyType) IS
2 BEGIN
3 FOR i IN 1..t_in.count LOOP
4 dbms_output.put_line(t_in(i));
5 END LOOP;
6 END;
7 /
Procedure created
SQL> DECLARE
2 v_t MyType;
3 BEGIN
4 v_t := MyType();
5 v_t.EXTEND(10);
6 v_t(1) := 'this is a test';
7 v_t(2) := 'A second test line';
8 testing(v_t);
9 END;
10 /
this is a test
A second test line
To expand on my comment to #dcp's answer, here's how you could implement the solution proposed there if you wanted to use an associative array:
SQL> CREATE OR REPLACE PACKAGE p IS
2 TYPE p_type IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
3
4 PROCEDURE pp (inp p_type);
5 END p;
6 /
Package created
SQL> CREATE OR REPLACE PACKAGE BODY p IS
2 PROCEDURE pp (inp p_type) IS
3 BEGIN
4 FOR i IN 1..inp.count LOOP
5 dbms_output.put_line(inp(i));
6 END LOOP;
7 END pp;
8 END p;
9 /
Package body created
SQL> DECLARE
2 v_t p.p_type;
3 BEGIN
4 v_t(1) := 'this is a test of p';
5 v_t(2) := 'A second test line for p';
6 p.pp(v_t);
7 END;
8 /
this is a test of p
A second test line for p
PL/SQL procedure successfully completed
SQL>
This trades creating a standalone Oracle TYPE (which cannot be an associative array) with requiring the definition of a package that can be seen by all in order that the TYPE it defines there can be used by all.
If the types of the parameters are all the same (varchar2 for example), you can have a package like this which will do the following:
CREATE OR REPLACE PACKAGE testuser.test_pkg IS
TYPE assoc_array_varchar2_t IS TABLE OF VARCHAR2(4000) INDEX BY BINARY_INTEGER;
PROCEDURE your_proc(p_parm IN assoc_array_varchar2_t);
END test_pkg;
CREATE OR REPLACE PACKAGE BODY testuser.test_pkg IS
PROCEDURE your_proc(p_parm IN assoc_array_varchar2_t) AS
BEGIN
FOR i IN p_parm.first .. p_parm.last
LOOP
dbms_output.put_line(p_parm(i));
END LOOP;
END;
END test_pkg;
Then, to call it you'd need to set up the array and pass it:
DECLARE
l_array testuser.test_pkg.assoc_array_varchar2_t;
BEGIN
l_array(0) := 'hello';
l_array(1) := 'there';
testuser.test_pkg.your_proc(l_array);
END;
/

Resources