Successfully compiled Oracle package fails when called with 'ORA-04063: package body "..." has errors - oracle

I have a pkg that I use to keep report oriented code CMS_REPORTS.
I added a procedure to return a ref cursor and the pkg compiles fine, but fails when I call the proc to test it with:
ORA-04063: package body "CMS.CMS_REPORTS" has errors
ORA-06508: PL/SQL: could not find program unit being called: "CMS.CMS_REPORTS"
I've removed the orig proc and replaced it with this to keep things simple - same problem.
The proc is this:
procedure test_ref_cur(p_testno in number,
p_cur in out ref_cur) as
begin
open p_cur for
select p_testno + 1 from dual;
end test_ref_cur;
I have defined the ref cursor in the pkg spec like this:
type ref_cur is ref cursor;
procedure test_ref_cur(p_testno in number,
p_cur in out ref_cur);
I've tried all sorts of combinations of using ref cursor and sys_refcursor and all bring up the same error. If I remove the proc from the pkg, it works fine.
I'm beginning to think it's a system issue?
Has anyone else had this problem?
Regards
Dave

Hard to tell what is the issue here, since it doesn't look like we are seeing the relevant code.
So here are some thing I recommend to double check:
package and package body are there and are actually compiled without an exception
you are in the schema/user that contains package and package body.
There are no other objects with the same name, that might hide your package/package body
the procedure you try to call is present in package and package body.
remove all code from package + package body except a single trivial procedure and check if that works.
If you've done all that update the question with the results.

In order to achieve what you want you have to use SYS_REFCURSOR:
create procedure test_ref_cur(p_testno in number,
p_cur in out SYS_REFCURSOR) as
begin
open p_cur for
select p_testno + 1 from dual;
end test_ref_cur;
-- PROCEDURE TEST_REF_CUR compiled
... and example:
DECLARE
l_sysrc SYS_REFCURSOR;
l_num NUMBER;
procedure test_ref_cur(p_testno in number,
p_cur in out SYS_REFCURSOR) as
begin
open p_cur for
select p_testno + 1 from dual;
end test_ref_cur;
BEGIN
test_ref_cur(1, l_sysrc);
FETCH l_sysrc INTO l_num;
DBMS_OUTPUT.PUT_LINE(l_num);
END;
-- Result:
-- 2
Since Oracle 7.3 the REF CURSOR type has been available to allow
recordsets to be returned from stored procedures and functions. Oracle
9i introduced the predefined SYS_REFCURSOR type, meaning we no longer
have to define our own REF CURSOR types.
Source: http://www.oracle-base.com/articles/misc/using-ref-cursors-to-return-recordsets.php

Related

Call a Package from Apex passing two values

I'm using Oracle 11.g and Apex 4.2.
I have an Oracle package (body and spec) that works fine with a stub of test data. My next step is to call this package from an Apex screen. I'd like to pass in two variables:
mail_event_prim_key - a number
mail_list_name - a varchar2(1024)
I have two questions.
1) How do I construct the package to receive two IN-only values from Apex? No values need to be returned to Apex.
2) How do I call this from Apex. I assume I'd use a PL/SQL function to do so.
My current package spec is:
create or replace PACKAGE "PKG_MAIL_SEND" as
PROCEDURE proc_mail_send;
PROCEDURE proc_job_mail_send;
PROCEDURE proc_kill_job_mail;
end PKG__MAIL_SEND;
A portion of my package body is:
create or replace PACKAGE BODY "PKG_MAIL_SEND" as
PROCEDURE proc_parish_mail_send
is
mail_event_prim_key number;
mail_list_name varchar2(1024);
crlf varchar2(2) := CHR(10) || CHR(13);
l_html clob;
l_html_header varchar2(1024);
l_html_body clob;
l_html_footer varchar2(1024);
l_html_total clob;
l_addresses DBMS_SQL.varchar2_table;
l_from varchar2(1024);
l_to varchar2(2048);
l_subject varchar2(2048);
l_smtp_hostname varchar2(2048);
l_smtp_port varchar2(2048);
l_smtp_username varchar2(2048);
l_smtp_password varchar2(2048);
BEGIN
-- Initialize Variables
mail_event_prim_key := '1';
mail_list_name := 'My List Name';
Select lu_value into l_smtp_hostname from hymn_lookup where lu_type = 'smtp_hostname';
-- Lots more code here
End proc_mail_send;
-- Code for PROCEDURE proc_job_mail_send;
-- Code for PROCEDURE proc_kill_job_mail;
end PKG_MAIL_SEND;
Thanks for looking at this.
To receive 2 values in package you obviously need procedure with 2 parameters. For example for your package:
create or replace PACKAGE "PKG_MAIL_SEND" as
-- let this procedure have 2 parameters:
PROCEDURE proc_mail_send(p_address in varchar2, p_topic in varchar2);
PROCEDURE proc_job_mail_send;
PROCEDURE proc_kill_job_mail;
end PKG__MAIL_SEND;
First way
After that click on Create page (or Create region), select type Form, then Form on Procedure. Pass all steps of wizard. After that you will have a region with fields for every parameters of procedure and button to run procedure.
Second way
Create page process. Write in source of process anonymous block, call your procedure from that block (here you need to create manually all things that apex creates automatically in first way - process, items, buttons, etc.).
Third way
Call procedure from dynamic action (it is bit more harder way if you are new to APEX).
Forth way
Call using ajax (procedure will be called inside application process). It is the most hard way, it is used rarely.

I am not getting the error in the package code of Oracle given below:

When I am trying to execute this code I am getting the following error. I am not able to resolve this problem - what am I missing?
SHOW ERRORS;
Errors for PACKAGE BODY PKG_VIEW_LEDGER:
LINE/COL ERROR
-------- -----------------------------------------------------------------
0/0 PL/SQL: Compilation unit analysis terminated
1/14 PLS-00304: cannot compile body of 'PKG_VIEW_LEDGER' without its
specification
1/14 PLS-00905: object SYSTEM.PKG_VIEW_LEDGER is invalid
--- code below ---
CREATE OR REPLACE PACKAGE PKG_VIEW_LEDGER AS
TYPE cur_view_data is REF CURSOR;
PROCEDURE sp_view_ledger(person IN VARCHAR2, cur_inout IN OUT cur_view_data);
END PRG_VIEW_LEDGER;
/
CREATE OR REPLACE PACKAGE BODY PKG_VIEW_LEDGER AS
PROCEDURE sp_view_ledger( person IN VARCHAR2, cur_inout IN OUT cur_view_data)
IS
tmp_cursor cur_view_data;
BEGIN
OPEN tmp_cursor FOR
select * from ledger where person like '%" + myArg.ToUpper + "%';
cur_inout := tmp_cursor;
END sp_view_ledger;
END PKG_VIEW_LEDGER;
/
First, based on the error, you are creating the package in the SYSTEM schema. Don't do that. SYS and SYSTEM are special and should be used only by Oracle for objects delivered by Oracle. You'll need a new schema where you can create your own objects. SYS and SYSTEM are special and do not always behave the same way that normal schemas do so various bits of functionality will appear not to work correctly if you use these schemas.
Second, when you are defining the package specification, your END does not match your CREATE. You are creating PKG_VIEW_LEDGER while you are ending PRG_VIEW_LEDGER. PKG != PRG. If you had a show errors after creating the package specification, the error would tell you this.
I have modified the code and its working fine. Please try it from your side and let me know. Thanks.
CREATE OR REPLACE PACKAGE PKG_VIEW_LEDGER AS
TYPE cur_view_data is REF CURSOR;
PROCEDURE sp_view_ledger(person IN VARCHAR2, cur_inout IN OUT cur_view_data);
END PkG_VIEW_LEDGER;
--------------------------------------------------------------
Package created.
--------------------------------------------------------------
CREATE OR REPLACE PACKAGE PKG_VIEW_LEDGER AS
TYPE cur_view_data is REF CURSOR;
PROCEDURE sp_view_ledger(person IN VARCHAR2, cur_inout IN OUT cur_view_data);
END PRG_VIEW_LEDGER;
/
CREATE OR REPLACE PACKAGE BODY PKG_VIEW_LEDGER AS
PROCEDURE sp_view_ledger( person IN VARCHAR2, cur_inout IN OUT cur_view_data)
IS
tmp_cursor cur_view_data;
BEGIN
OPEN tmp_cursor FOR
select * from avrajit;
cur_inout := tmp_cursor;
END sp_view_ledger;
END PKG_VIEW_LEDGER;
/
-----------------------------------------------------
Package Body created.
0.04 seconds

What to pass as a SYS_REFCURSOR argument

Here I have a stored procedure in Oracle:
CREATE OR REPLACE PROCEDURE StP_COMPS
IS
CV_1 SYS_REFCURSOR;
BEGIN
OPEN CV_1 FOR SELECT * FROM COMPUTERS;
END;
When I execute the procedure like EXEC SP_COMPS I get no error, the SQL Developer just shows "ananymous block completed". Then I change the procedure to a
CREATE OR REPLACE PROCEDURE SP_COMPS
(cv_1 OUT SYS_REFCURSOR)
IS
BEGIN
OPEN CV_1 FOR SELECT * FROM COMPUTERS;
END;
and when I execute I get error stating that the number of type of the arguments are wrong. I'm very curious what I could send as an argument to the procedure if it's just an output parameter. I want to get the result set of the query run inside the procedure. What am I doing wrong here?
P.S. When I try to run the procedure by right clicking the procedure and selecting Run I get:
DECLARE
CV_2 sys_refcursor;
BEGIN
SP_COMPS(
CV_2 => CV_2
);
:CV_2 := CV_2; -- <--Can't understand this part
END;
You have a variable, you should execute the procedure like:
DECLARE
CV_1 SYS_REFCURSOR;
BEGIN
SP_COMPS(CV_1);
--use cv_1
END;
UPDATE(after OP update): That's a simple template for testing. As explained here: Easiest method to test an Oracle Stored Procedure, just run that code, and select ref_cursor as type of cv2 variable.

Cursor design and refactoring question

I have many cursors that all return rows with the same fields: a numeric ID field and an XMLType field. Every time I access one of these cursors (each cursor has now got its own function for access), I go through the same pattern:
--query behind cursor is designed to no more than one row.
for rec in c_someCursor(in_searchKey => local_search_key_value) loop
v_id := rec.ID
v_someXMLVar := rec.XMLDataField
end loop;
if v_someXMLVar is null then
/* A bunch of mostly-standard error handling and logging goes here */
end if;
exception
/* all cursor access functions have the same error-handling */
end;
As the pattern became more obvious, it made sense to centralize it in a single function:
function fn_standardCursorAccess(in_cursor in t_xmlCursorType, in_alt in XMLType) return XMLType is
v_XMLData XMLType;
begin
dbms_application_info.set_module(module_name => $$PLSQL_UNIT, action_name => 'fn_standardCursorAccess');
loop
fetch in_cursor
into v_XMLData;
exit when in_cursor%notfound;
end loop;
/*some additional standard processing goes here*/
return v_XML;
exception
/*standard exception handling happens here*/
end;
The problem I've run into is in calling this function. I now have to call it like this:
open v_curs for select /*blah blah blah*/ where key_field = x and /*...*/;
v_data := fn_standardCursorAccess(v_curs,alt);
close v_curs;
What I'd like to do is call it like this:
open v_curs for c_getSomeData(x);
v_data := fn_standardCursorAccess(v_curs,alt);
close v_curs;
...reason being to minimize the amount of changes to my code (I don't want to have to cut/paste all these cursors to the functions that depend on them, and in the case where multiple functions depend on the same cursor, I'll have to wrap that in a new function).
Unfortunately, this does not work, Oracle returns an error saying
Error: PLS-00222: no function with name 'C_GETSOMEDATA' exists in this scope
Is what I'm trying to do even possible?
(Oracle version is 10.2)
EDIT:
I think a better way to describe what I'm doing is pass a reference to an explicit cursor to a function that will perform some common routines on the data returned by the cursor.
It appears that I cannot use an open-for statement with an explcit cursor, is there any other way to get a reference to an explicit cursor so I can pass that reference to a function? Maybe there is some other way I could approach this problem?
EDIT:
Copying and pasting from my earlier reply to R Van Rijn's reply:
I tried declaring the cursor in the package specification, and referencing it with the package name: open v_curs for PKG.c_getSomeData(x);... This gives me a new error, saying that PKG.c_getSomeData must be a function or array to be used that way.
UPDATE:
I spoke to our DBA here, he says it is not possible to have a ref cursor point to an explicit cursor. It looks like I can't do this after all. Bummer. :(
concerning the Error PLS-00222:
An identifier being referenced as a function 'c_getSomeData' was not declared or actually represents another object (for example, it might have been declared as a procedure).
Check the spelling and declaration of the identifier. Also confirm that the declaration is placed correctly in the block structure
This means you must create a function that actually returns some value(s).
Does this test script and output represent what you are trying to do? Instead of open v_curs for c_getSomeData(x); I'm setting the cursor variable = to the output from the function.
Our Test Data:
set serveroutput on
--create demo table
drop table company;
create table company
(
id number not null,
name varchar2(40)
);
insert into company (id, name) values (1, 'Test 1 Company');
insert into company (id, name) values (2, 'Test 2 Company');
insert into company (id, name) values (3, 'Test 3 Company');
commit;
Create Packages
create or replace package test_pkg as
type cursor_type is ref cursor;
function c_getSomeData(v_companyID number) return cursor_type;
end test_pkg;
/
create or replace package body test_pkg as
function c_getSomeData(v_companyID number) return cursor_type
is
v_cursor cursor_type;
begin
open v_cursor for
select id,
name
from company
where id = v_companyID;
return v_cursor;
end c_getSomeData;
end test_pkg;
/
Run Our Procedure
declare
c test_pkg.cursor_type;
v_id company.id%type;
v_name company.name%type;
begin
c := test_pkg.c_getSomeData(1);
loop
fetch c
into v_id, v_name;
exit when c%notfound;
dbms_output.put_line(v_id || ' | ' || v_name);
end loop;
close c;
end;
/
1 | Test 1 Company
PL/SQL procedure successfully completed.
I confess to finding your requirements a trifle hard to divine. You have posted a lot of code, but as I suggested in my comment, not the parts which would illuminate the problem. So possibly the following is way off-beam. But it is an interesting issue.
The following code shows how we can define a common, geneneric REF CURSOR, populate it with specific data from different queries, and then process them in a standardised fashion. Again, I apologise if this does not fit your business logic; if such is the case, please edit your question to explain where I have made a bloomer..
Here is the generic ref cursor. ...
create or replace package type_def is
type xml_rec is record (id number, payload xmltype);
type xml_cur is ref cursor return xml_rec;
end type_def;
/
and here is the standatd processor
create or replace procedure print_xml_cur
( p_cur in type_def.xml_cur )
is
lrec type_def.xml_rec;
begin
loop
fetch p_cur into lrec;
exit when p_cur%notfound;
dbms_output.put_line('ID='||lrec.id);
dbms_output.put_line('xml='||lrec.payload.getClobVal());
end loop;
close p_cur;
end print_xml_cur;
/
Two procedures which return the standard cursor with different data....
create or replace function get_emp_xml
( p_id in emp.deptno%type )
return type_def.xml_cur
is
return_value type_def.xml_cur;
begin
open return_value for
select deptno
, sys_xmlagg(sys_xmlgen(ename))
from emp
where deptno = p_id
group by deptno;
return return_value;
end get_emp_xml;
/
create or replace function get_dept_xml
( p_id in dept.deptno%type )
return type_def.xml_cur
is
return_value type_def.xml_cur;
begin
open return_value for
select deptno
, sys_xmlagg(sys_xmlgen(dname))
from dept
where deptno = p_id
group by deptno;
return return_value;
end get_dept_xml;
/
Now let's put it all together ....
SQL> set serveroutput on size unlimited
SQL>
SQL> exec print_xml_cur(get_emp_xml(40))
ID=40
xml=<?xml
version="1.0"?>
<ROWSET>
<ENAME>GADGET</ENAME>
<ENAME>KISHORE</ENAME>
</ROWSET>
PL/SQL procedure successfully completed.
SQL> exec print_xml_cur(get_dept_xml(20))
ID=20
xml=<?xml version="1.0"?>
<ROWSET>
<DNAME>RESEARCH</DNAME>
</ROWSET>
PL/SQL procedure successfully completed.
SQL>
OK, so the short answer from Oracle is: "can't be done!"
The short answer from me is: "Yeah - like Oracle is gonna stop me! So yes you can....but you need to be sneaky ... oh yes, and there is a 'but' or two....in fact...ugh!"
So, how can you pass your explicit cursor by reference? By nesting it into another cursor using the CURSOR() construct!
e.g.)
CREATE OR REPLACE package CFSDBA_APP.test_Cursor
as
function get_cursor(ed_id number) return sys_refcursor;
end;
/
CREATE OR REPLACE package body CFSDBA_APP.test_Cursor
as
function get_cursor(ed_id number) return sys_refcursor
is
test_Cur sys_refcursor;
cursor gettest is
select CURSOR( -pass our actual query back as a nested CURSOR type
select ELCTRL_EVNT_ELCTRL_DISTRCT_ID,
ELECTORAL_DISTRICT_ID,
ELECTORAL_EVENT_ID
from ELCTRL_EVNT_ELCTRL_DISTRCT
where electoral_District_id = ed_id)
from dual;
begin
open gettest;
fetch gettest into test_Cur;
return test_Cur;
end;
end;
/
So what is the problem with this solution? It has a leak! The outer gettest cursor is never closed, because we don't close it and the client will only close the reference to the nested cursor that was selected for them - not the main cursor. And we can't close it automatically because closign the parent would force closing the nested cursor that you have returned by reference - and it is entirely likely that the client has not done using it.
So we have to leave a cursor open in order to return the nested cursor.
And if the user tried calling get_Cursor again with a new value of ed_id, they would discover that the session persistence in the package meant that the cursor handle is still in use and an error would be raised.
Now, we could fix that by first checking and closing the explicit cursor:
if gettest%isopen then
close gettest;
end if;
open gettest;
fetch gettest into test_Cur;
return test_Cur;
But still - what if the user never calls this again? How long 'til Oracle garbage-collects the cursor? And how many users running how many sessions calling how many functions that use this construct will be leaving cursors open after they are done with them? Better count on a huuuuuge overhead to leave all those open cursors layin' about!
No, you would need to have the users do a callback to explicitely close it or you would be clogging the database. But doing this would require changing the scope of the explicit cursor so that both functions can access it: So we need to make it at package scope, not function scope
CREATE OR REPLACE package CFSDBA_APP.test_Cursor
as
function get_cursor(ed_id number) return sys_refcursor;
function close_cursor return sys_refcursor;
end;
/
CREATE OR REPLACE package body CFSDBA_APP.test_Cursor
as
cursor l_gettest(p_ed_id in number) is
select CURSOR(
select ELCTRL_EVNT_ELCTRL_DISTRCT_ID, ELECTORAL_DISTRICT_ID, ELECTORAL_EVENT_ID
from ELCTRL_EVNT_ELCTRL_DISTRCT
where electoral_District_id = p_ed_id)
from dual;
function get_cursor(ed_id number) return sys_refcursor
is
l_get_Cursor sys_refcursor;
begin
open l_gettest (ed_id);
fetch l_gettest into l_get_Cursor;
return l_get_cursor;
end;
function close_cursor return sys_refcursor
is
begin
if l_gettest%isopen then
close l_gettest;
end if;
return pkg_common.generic_success_cursor;
end;
end;
/
OK, plugged the leak. Except it cost us a network round trip instead of the hard parse,...oh wait - and also except embedding a bind variable into an explicit cursor declared at this level is probably going to cause scoping issues of its own which was the reason we wanted to do this in the first place!
Oh, and in a session-pooling environment can two users step on each other's cursors? IF they aren't very carefull about doing an open-fetch-close before returning the session to the pool - we could wind up with some really interesting (and impossible to debug) results!
And how much do you trust the maintainers of the client code to be extra-diligent on this? YEah - me too.
So the short answer is: Yes, with a bit of sneakiness it could be done despite Oracle saying it can't.
The better answer is: But please don't! The extra round trip and potential for memory leaks and client code errors causing data problems makes this a very scary proposition.
It appears that what I wanted to do (have an open-for statement reference an existing explicit cursor) is simply not allowed in Oracle. :(

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