PLS-00201 – identifier must be declared, passing a collection to procedure - oracle

I'm trying to pass a collection to procedure, but when I compile the package, I'll get this message:"PLS-00201 – identifier must be declared".
this is my code:
create or replace package PACK_DW_TEMP
as
procedure A (.......);
--
procedure B (error_list in out l_error);
end PACK_DW_TEMP;
In the package body I've created the collection in the procedure A and passed it to procedure B
create or replace package body PACK_DW_TEMP
as
procedure A ( ........ )
as
begin
declare
type error IS RECORD(
cod_error NUMBER,
descr_error VARCHAR2(100)
);
type l_errori is table of error;
error_list l_error := l_error();
begin
procedure B(error_list);
end;
end;
Into procedure B:
procedure B ( error_list in out l_error )
as
begin
declare
i NUMBER;
type err IS RECORD(
cod_error NUMBER,
descr_error VARCHAR2(100)
);
type l_err is table of err;
err_list l_err := l_err();
begin
i := 0;
for i in 1..5 loop
err_list(i).cod_err := error_list(i).cod_error;
err_list(i).descr_err := error_list(i).descr_error;
end loop;
end;
end;
My target is to pass the collection to procedure B and to assegn values to the new collection.

You're defining a type called l_err in the package body but in procedure B you're trying to use a type called l_error. Also, you have to define l_error in the package spec, prior to its first use:
create or replace package PACK_DW_TEMP
as
type err IS RECORD(
cod_error NUMBER,
descr_error VARCHAR2(100)
);
type l_error is table of err;
procedure A (.......);
--
procedure B (error_list in out l_error);
end PACK_DW_TEMP;
and remove the definitions of type_err, l_errori, and l_err from the package body.

A type declared locally to a procedure and another type declared locally to another procedure with exactly the signature are not the same types and you cannot pass one to the other. You need to create the type externally to the procedures rather than internally.
Also, you cannot use the type in the signature for the package when it is not declared except internally to the procedure which is declared in the body of the package.
create or replace package PACK_DW_TEMP
as
TYPE error IS RECORD(
cod_error NUMBER,
descr_error VARCHAR2(100)
);
type l_error is table of error;
procedure A;
procedure B (error_list in out l_error);
end PACK_DW_TEMP;
/
and
create or replace package body PACK_DW_TEMP
as
procedure A
as
error_list l_error := l_error();
begin
error_list.EXTEND(2);
error_list(1).cod_error := 1;
error_list(1).descr_error := 'DESCR1';
error_list(2).cod_error := 2;
error_list(2).descr_error := 'DESCR2';
B(error_list);
FOR i IN 1 .. error_list.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(
error_list(i).cod_error || ': ' || error_list(i).descr_error
);
END LOOP;
end;
procedure B ( error_list in out l_error )
as
begin
error_list.EXTEND;
error_list(error_list.COUNT).cod_error := 99;
error_list(error_list.COUNT).descr_error := 'DESCR99';
end;
end;
/
Then you can call it using:
BEGIN
PACK_DW_TEMP.A();
END;
/
Which outputs:
1: DESCR1
2: DESCR2
99: DESCR99
db<>fiddle here

Related

push procedure pop function with sequence number in oracle

I want to write code for push with procedure and pop with Function .
create or replace package pushpop_demo as
procedure push(val varchar2);
function pop return varchar2;
end pushpop_demo;
create or replace package body pushpop_demo as
subtype my_string_subtype is varchar2(100);
type varchar2_ntt is table of my_string_subtype;
stuff varchar2_ntt := varchar2_ntt();
procedure push(val varchar2)
is
begin
stuff.extend;
stuff(stuff.last) := val;
end push;
function pop return varchar2
is
subtype my_string_subtype varchar2(100);
begin
if stuff is not empty then
val := stuff(stuff.last);
stuff.delete(stuff.last);
end if;
return val;
end pop ;
but I get error , my question is how can I do this problem with sequence number ??? or other solution .my code does not run anyway . also I do not want use the package .just with procedure and Function .please help me
You need / statement terminators to terminate each PL/SQL block.
You are missing an END; statement at the end of the package body.
You have not declared the VAL variable in the function.
You do not want to re-declare the my_string_subtype type (as it will shadow sub-type declared in the package body).
You can initialise the collection in the declaration section or an alternative is to initialise it in the BEGIN ... END section of the package body.
create or replace package pushpop_demo as
procedure push(val varchar2);
function pop return varchar2;
end pushpop_demo;
/
create or replace package body pushpop_demo as
subtype my_string_subtype is varchar2(100);
type varchar2_ntt is table of my_string_subtype;
stuff varchar2_ntt; -- := varchar2_ntt();
procedure push(val varchar2)
is
begin
stuff.extend;
stuff(stuff.last) := val;
end push;
function pop return varchar2
is
val my_string_subtype;
begin
if stuff is not empty then
val := stuff(stuff.last);
stuff.delete(stuff.last);
end if;
return val;
end pop;
BEGIN
stuff := varchar2_ntt();
END;
/
Then you can use:
BEGIN
pushpop_demo.push('World');
pushpop_demo.push('Hello');
END;
/
and then:
BEGIN
DBMS_OUTPUT.PUT_LINE(pushpop_demo.pop());
DBMS_OUTPUT.PUT_LINE(pushpop_demo.pop());
END;
/
Outputs:
Hello
World
fiddle

what is wrong passing a record type as parameter

I have the following code snippets:
CREATE OR REPLACE PACKAGE pkg_test1 IS
TYPE t_rec1 is RECORD(c1 number);
FUNCTION f1(p1 t_rec1 ) RETURN VARCHAR2;
END pkg_test1;
CREATE OR REPLACE PACKAGE BODY pkg_test1 IS
FUNCTION f1 (p1 t_rec1) return varchar2 is
BEGIN
RETURN 'a';
END f1;
END pkg_test1;
DECLARE
TYPE t_rec IS RECORD (c1 NUMBER);
v_rec t_rec;
v_var VARCHAR2(15);
BEGIN
v_rec.c1 := 1;
v_var := pkg_test1.f1 (v_rec);
END;
When I execute the anonym code, I received the following error:
PLS-00306: wrong number or types of arguments in call to 'F1'
Can someone help me to see the mistake I make, please?
The type you declare in your anonymous block looks the same to you, but to Oracle it's a completely independent and incompatible type.
From the documentation:
A RECORD type defined in a package specification is incompatible with an identically defined local RECORD type.
You don't need it though; just use the package type:
DECLARE
-- don't create a new type....
--TYPE t_rec IS RECORD (c1 NUMBER);
--v_rec t_rec;
-- use the package type instead
v_rec pkg_test1.t_rec1;
v_var VARCHAR2(15);
BEGIN
v_rec.c1 := 1;
v_var := pkg_test1.f1 (v_rec);
END;
db<>fiddle

How call a procedure with a TYPE RECORD?

I did a data insertion procedure in a package and I want to use it together with a RECORD TYPE but I don't know what to call it.
I want to at least be able to enter 'codigo' with the other values in null
CREATE TABLE TB_CRUD_MAC"
( "K_CODIGO" NUMBER(10,0),
"A_NUMNIT" VARCHAR2(11 BYTE),
"N_NOMBRE" VARCHAR2(11 BYTE),
"N_APELLI" VARCHAR2(11 BYTE),
"F_FECHA" DATE,
"I_ESTADO" VARCHAR2(1 BYTE),
"K_CLASIF" VARCHAR2(1 BYTE) )
create or replace PACKAGE PK_CRUD_MAC AS
TYPE R_REGISTRO IS RECORD (
codigo TB_CRUD_MAC.K_CODIGO%TYPE,
numnit TB_CRUD_MAC.A_NUMNIT%TYPE,
nombre TB_CRUD_MAC.N_NOMBRE%TYPE,
apelli TB_CRUD_MAC.N_APELLI%TYPE,
fecha TB_CRUD_MAC.F_FECHA%TYPE,
estado TB_CRUD_MAC.I_ESTADO%TYPE,
clasif TB_CRUD_MAC.K_CLASIF%TYPE
);
PROCEDURE PR_INSERT_REGISTRO (P_R_REGISTRO R_REGISTRO);
END;
create or replace PACKAGE BODY PK_CRUD_MAC AS
PROCEDURE PR_INSERT_REGISTRO (P_R_REGISTRO R_REGISTRO)
IS
BEGIN
INSERT INTO TB_CRUD_MAC VALUES P_R_REGISTRO;
END;
END;
Yeah, you cannot do that. You have to pass in a complete record, example:
declare
reg PK_CRUD_MAC.R_REGISTRO;
begin
reg.codigo := 6;
PK_CRUD_MAC.PR_INSERT_REGISTRO(reg);
end;
However, if all you are trying to do is insert the record with the same type as the table column definitions, you do not need to create your own pl/sql record. You can do as below:
create or replace PACKAGE PK_CRUD_MAC2 AS
PROCEDURE PR_INSERT_REGISTRO (P_R_REGISTRO TB_CRUD_MAC%ROWTYPE);
END;
create or replace PACKAGE BODY PK_CRUD_MAC2 AS
PROCEDURE PR_INSERT_REGISTRO (P_R_REGISTRO TB_CRUD_MAC%ROWTYPE)
IS
BEGIN
INSERT INTO TB_CRUD_MAC VALUES P_R_REGISTRO;
END;
END;
You CANNOT do what you are attempting, at least the way you are attempting. You have defined you procedure as taking a record structure as input variable but then calling it with a single value. That is the wrong type error you get.
You can however overload the procedure in the package.
create or replace package pk_crud_mac as
type r_registro is record (
codigo tb_crud_mac.k_codigo%type,
numnit tb_crud_mac.a_numnit%type,
nombre tb_crud_mac.n_nombre%type,
apelli tb_crud_mac.n_apelli%type,
fecha tb_crud_mac.f_fecha%type,
estado tb_crud_mac.i_estado%type,
clasif tb_crud_mac.k_clasif%type
);
procedure pr_insert_registro (p_r_registro r_registro);
procedure pr_insert_registro (codigo tb_crud_mac.k_codigo%type);
end;
create or replace package body pk_crud_mac as
procedure pr_insert_registro (p_r_registro r_registro)
is
begin
insert into tb_crud_mac values p_r_registro;
end;
procedure pr_insert_registro (p_codigo tb_crud_mac.k_codigo%type)
is
begin
insert into tb_crud_mac values (p_codigo);
end;
end;
Add a function as a constructor that will initialize your record. Define all parameters as optional. And call. For example
create table TB_CRUD_MAC(
K_CODIGO number(10)
,A_NUMNIT varchar2(11)
,N_NOMBRE varchar2(11)
,N_APELLI varchar2(11)
,F_FECHA date
,I_ESTADO varchar2(1)
,K_CLASIF varchar2(1)
);
create or replace package PK_CRUD_MAC
is
subtype R_REGISTRO is TB_CRUD_MAC%rowtype;
function New (pK_CODIGO TB_CRUD_MAC.K_CODIGO%type := null
,pA_NUMNIT TB_CRUD_MAC.A_NUMNIT%type := null
,pN_NOMBRE TB_CRUD_MAC.N_NOMBRE%type := null
,pN_APELLI TB_CRUD_MAC.N_APELLI%type := null
,pF_FECHA TB_CRUD_MAC.F_FECHA%type := null
,pI_ESTADO TB_CRUD_MAC.I_ESTADO%type := null
,pK_CLASIF TB_CRUD_MAC.K_CLASIF%type := null) return R_REGISTRO;
procedure PR_INSERT_REGISTRO (P_R_REGISTRO R_REGISTRO);
end;
/
create or replace package body PK_CRUD_MAC
is
function New (pK_CODIGO TB_CRUD_MAC.K_CODIGO%type := null
,pA_NUMNIT TB_CRUD_MAC.A_NUMNIT%type := null
,pN_NOMBRE TB_CRUD_MAC.N_NOMBRE%type := null
,pN_APELLI TB_CRUD_MAC.N_APELLI%type := null
,pF_FECHA TB_CRUD_MAC.F_FECHA%type := null
,pI_ESTADO TB_CRUD_MAC.I_ESTADO%type := null
,pK_CLASIF TB_CRUD_MAC.K_CLASIF%type := null) return R_REGISTRO
is
vR_REGISTRO R_REGISTRO;
begin
vR_REGISTRO.K_CODIGO := pK_CODIGO;
vR_REGISTRO.A_NUMNIT := pA_NUMNIT;
vR_REGISTRO.N_NOMBRE := pN_NOMBRE;
vR_REGISTRO.N_APELLI := pN_APELLI;
vR_REGISTRO.F_FECHA := pF_FECHA ;
vR_REGISTRO.I_ESTADO := pI_ESTADO;
vR_REGISTRO.K_CLASIF := pK_CLASIF;
return vR_REGISTRO;
end;
procedure PR_INSERT_REGISTRO (P_R_REGISTRO R_REGISTRO)
is
begin
insert into TB_CRUD_MAC values P_R_REGISTRO;
end;
end;
/
begin
PK_CRUD_MAC.PR_INSERT_REGISTRO(PK_CRUD_MAC.New(pK_CODIGO => :K_CODIGO));
-- commit;
end;
/

how to manually cache the values of function calls in 10g

I have the following code
CREATE OR REPLACE FUNCTION slow_function (p_in IN NUMBER)
RETURN NUMBER
AS
BEGIN
DBMS_LOCK.sleep(1);
RETURN p_in;
END;
/
CREATE OR REPLACE PACKAGE cached_lookup_api AS
FUNCTION get_cached_value (p_id IN NUMBER)
RETURN NUMBER;
PROCEDURE clear_cache;
END cached_lookup_api;
/
CREATE OR REPLACE PACKAGE BODY cached_lookup_api AS
TYPE t_tab IS TABLE OF NUMBER
INDEX BY BINARY_INTEGER;
g_tab t_tab;
g_last_use DATE := SYSDATE;
g_max_cache_age NUMBER := 10/(24*60); -- 10 minutes
-- -----------------------------------------------------------------
FUNCTION get_cached_value (p_id IN NUMBER)
RETURN NUMBER AS
l_value NUMBER;
BEGIN
IF (SYSDATE - g_last_use) > g_max_cache_age THEN
-- Older than 10 minutes. Delete cache.
g_last_use := SYSDATE;
clear_cache;
END IF;
BEGIN
l_value := g_tab(p_id);
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- Call function and cache data.
l_value := slow_function(p_id);
g_tab(p_id) := l_value;
END;
RETURN l_value;
END get_cached_value;
-- -----------------------------------------------------------------
-- -----------------------------------------------------------------
PROCEDURE clear_cache AS
BEGIN
g_tab.delete;
END;
-- -----------------------------------------------------------------
END cached_lookup_api;
/
I want to pass two parameters "pi_value1" and "pi_value2" both of varchar2 to the function slow_function instead of "p_in". Is is possible to cache the results with two in parameters in oracle 10g .
the above code works fine with 1 parameter.
Please any one explain?
You'd need to create a two-dimensional array type to cache the values. Something along the lines of this (omitting the cache expiration code since that isn't changing)
CREATE OR REPLACE PACKAGE BODY cached_lookup_api
AS
TYPE t_pi_value2_tbl IS TABLE OF NUMBER
INDEX BY VARCHAR2(100);
TYPE t_cache IS TABLE OF t_pi_value2_tbl
INDEX BY VARCHAR2(100);
g_cache t_cache;
FUNCTION get_cached_value( p_pi_value1 IN VARCHAR2,
p_pi_value2 IN VARCHAR2 )
IS
BEGIN
RETURN g_cache(p_pi_value1)(p_pi_value2);
EXCEPTION
WHEN no_data_found
THEN
g_cache(p_pi_value1)(p_pi_value2) := slow_function( p_pi_value1, p_pi_value2 );
RETURN g_cache(p_pi_value1)(p_pi_value2);
END;
END;

How to call an Oracle PL/SQL object super method

I'd like to call an overridden PL/SQL method. Here's an example:
-- super class
create or replace type test as object
(
n number,
member procedure proc(SELF in out nocopy test, s varchar2)
)
alter type test not final
/
create or replace type body test is
member procedure proc(SELF in out nocopy test, s varchar2) is
begin
dbms_output.put_line('test1: n='||nvl(self.n, 'null')||' s='||s);
self.n := to_number(s);
end;
end;
/
-- derived class
create or replace type test2 under test
(
overriding member procedure proc(SELF in out nocopy test2, s varchar2)
)
/
Now I want to invoke the inherited version of the proc method. When I try to do an explicit cast like treat(self as test).proc(s); it won't compile because of PLS-00363: expression 'SYS_TREAT' cannot be used as an assignment target
The type body compiles when I use a local variable:
create or replace type body test2 is
overriding member procedure proc(SELF in out nocopy test2, s varchar2) is
O test;
begin
O := treat(self as test);
O.proc(s);
end;
end;
/
But when I run my example like this
declare
obj test2;
begin
obj := test2(0);
obj.proc('1');
end;
...it throws ORA-21780: Maximum number of object durations exceeded.
Is there any way to call test::proc (without serializing/deserializing)?
And... after proc has been called, how can any changed attributes (namely n) be reflected in obj ?
Update (Thanks, tbone):
I changed the organization of my methods using template methods ('before' and 'after'). I add them whenever I need to extend a method.
create or replace type test as object
(
n number,
member procedure proc (SELF in out nocopy test, s varchar2),
member procedure afterProc (SELF in out nocopy test, s varchar2)
member procedure beforeProc(SELF in out nocopy test, s varchar2),
)
not final
/
create or replace type body test is
member procedure proc(SELF in out nocopy test, s varchar2) is
begin
beforeProc(s);
dbms_output.put_line('test1: n='||nvl(n, 'null')||' s='||s);
n := to_number(s);
afterProc(s);
end;
member procedure afterProc (SELF in out nocopy test, s varchar2) is begin null; end;
member procedure beforeProc(SELF in out nocopy test, s varchar2) is begin null; end;
end;
/
To access the super methods, try either general invocation or generalized expression. For example, using a person supertype and student subtype:
CREATE OR REPLACE TYPE person_typ AS OBJECT (
idno number,
name varchar2(30),
phone varchar2(20),
MAP MEMBER FUNCTION get_idno RETURN NUMBER,
MEMBER FUNCTION show RETURN VARCHAR2)
NOT FINAL;
CREATE OR REPLACE TYPE BODY person_typ AS
MAP MEMBER FUNCTION get_idno RETURN NUMBER IS
BEGIN
RETURN idno;
END;
MEMBER FUNCTION show RETURN VARCHAR2 IS
BEGIN
-- function that can be overriden by subtypes MEMBER FUNCTION show RETURN VARCHAR2 IS BEGIN
RETURN 'Id: ' || TO_CHAR(idno) || ', Name: ' || name;
END;
END;
CREATE TYPE student_typ UNDER person_typ (
dept_id NUMBER,
major VARCHAR2(30),
OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2)
NOT FINAL;
CREATE TYPE BODY student_typ AS
OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2 IS
BEGIN
RETURN (self AS person_typ).show || ' -- Major: ' || major ;
END;
END;
-- Using Generalized Invocation
DECLARE
myvar student_typ := student_typ(100, 'Sam', '6505556666', 100, 'Math');
name VARCHAR2(100);
BEGIN
name := (myvar AS person_typ).show; --Generalized invocation
END;
-- Using Generalized Expression
DECLARE
myvar2 student_typ := student_typ(101, 'Sam', '6505556666', 100, 'Math');
name2 VARCHAR2(100);
BEGIN
name2 := person_typ.show((myvar2 AS person_typ)); -- Generalized expression
END;
EDIT:
If you are on 10g, you'll need to organize the functions a bit different, but same functionality from the child to call the super method:
CREATE TYPE BODY person_typ AS
MAP MEMBER FUNCTION get_idno RETURN NUMBER IS
BEGIN
RETURN idno;
END;
-- static function that can be called by subtypes
STATIC FUNCTION show_super (person_obj in person_typ) RETURN VARCHAR2 IS
BEGIN
RETURN 'Id: ' || TO_CHAR(person_obj.idno) || ', Name: ' || person_obj.name;
END;
-- function that can be overriden by subtypes
MEMBER FUNCTION show RETURN VARCHAR2 IS
BEGIN
RETURN person_typ.show_super ( SELF );
END;
END;
CREATE TYPE student_typ UNDER person_typ (
dept_id NUMBER,
major VARCHAR2(30),
OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2)
NOT FINAL;
CREATE TYPE BODY student_typ AS
OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2 IS
BEGIN
RETURN person_typ.show_super ( SELF ) || ' -- Major: ' || major ;
END;
END;
Now you'd call show_super() from student for the person method, or just show() for the student method.
From the docs, hope that helps.

Resources