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

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>

Related

ORA-00900: invalid SQL statement- when run a package in oracle 12c

I am using Oracle 12c database and trying to run a package using SQL commands.
CREATE OR REPLACE PACKAGE "PK_CP_OTM" as
FUNCTION F_CP_OPTIMIZATION (
v_current_day IN VARCHAR2,
v_branch_code IN VARCHAR2)
RETURN VARCHAR2;
END PK_CP_OTM;
When I try to execute it using:
DECLARE
BEGIN
EXECUTE IMMEDIATE PK_CP_OTM.F_CP_OPTIMIZATION('20190409','BRNCD001');
END;
It shows:
ORA-00900: invalid SQL statement
ORA-06512: at line 3
00900. 00000 - "invalid SQL statement"
Thanks for your help.
As #Littlefoot said, you don't need dynamic SQL here, you can make a static call; but as you are calling a function you do need somewhere to put the result of the call:
declare
l_result varchar2(30); -- make it a suitable size
begin
l_result := pk_cp_otm.f_cp_optimization('20190409','BRNCD001');
end;
/
In SQL*Plus, SQL Developer and SQLcl you can use the execute client command (which might have caused some confusion) and a bind variable for the result:
var result varchar2(30);
exec :result := pk_cp_otm.f_cp_optimization('20190409','BRNCD001');
print result
There's nothing dynamic here, so - why would you use dynamic SQL at all?
Anyway: if you insist, then you'll have to select the function into something (e.g. a local variable). Here's an example
First, the package:
SQL> set serveroutput on
SQL>
SQL> create or replace package pk_cp_otm
2 as
3 function f_cp_optimization (v_current_day in varchar2,
4 v_branch_code in varchar2)
5 return varchar2;
6 end pk_cp_otm;
7 /
Package created.
SQL> create or replace package body pk_cp_otm
2 as
3 function f_cp_optimization (v_current_day in varchar2,
4 v_branch_code in varchar2)
5 return varchar2
6 is
7 begin
8 return 'Littlefoot';
9 end;
10 end pk_cp_otm;
11 /
Package body created.
How to call the function?
SQL> declare
2 l_result varchar2 (20);
3 begin
4 execute immediate
5 'select pk_cp_otm.f_cp_optimization (''1'', ''2'') from dual'
6 into l_result;
7
8 dbms_output.put_line ('result = ' || l_result);
9 end;
10 /
result = Littlefoot
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.

Procedure in package specification

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 :)

ORA-06502: PL/SQL: numeric or value error: character to number conversion error appears on the first call

I have the below pl/sql procedure
PROCEDURE insert_p(
p_batch_rec IN ra_batches%rowtype,
p_batch_id OUT NOCOPY ra_batches.batch_id%type,
p_name OUT NOCOPY ra_batches.name%type
)
batch_id is NUMBER(18,0) and p_name is VARCHAR2(50 CHAR)
I'm calling the procedure with
insert_p (l_batch_rec, p_batch_id, p_name);
where p_batch_id:=NULL and p_name:=NULL
I get the conversion error only on the first time I run the procedure. If I run again without changes, it runs fine. To reproduce the error, I disconnect and connect again.
Any ideas why does this error come and how should I resolve the same???
Does the error happen in the initialization part of a package that is used by the function?
You should be able to easily find exactly where the error happens. By default, Oracle will display the object and the line number of the error. (Although, to my great frustration, I find that it is very common for people to write when others then [poor logging that throws out line number].)
SQL> --Create package
SQL> create or replace package test_package is
2 procedure test_procedure;
3 conversion_error number;
4 end;
5 /
Package created.
SQL> --Create package body. Note the initialization part that will fail.
SQL> create or replace package body test_package is
2 procedure test_procedure is
3 begin
4 null;
5 end;
6 begin
7 conversion_error := 'This is not a number';
8 end;
9 /
Package body created.
SQL> --This will fail the first time
SQL> begin
2 test_package.test_procedure;
3 end;
4 /
begin
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at "JHELLER.TEST_PACKAGE", line 7
ORA-06512: at line 2
SQL> --But will now work until you reconnect, or run DBMS_SESSION.RESET_PACKAGE.
SQL> begin
2 test_package.test_procedure;
3 end;
4 /
PL/SQL procedure successfully completed.
SQL>

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