Oracle PL/SQL package compilation with interdependent procedure - oracle

If I have procedure Proc1 and another Procedure Proc2 where proc1 depends on proc2 and proc2 depends on proc1 . I need to compile package pkg1 with both these procedures .
Oracile 9i.
How can I achieve it? any particular keyword is required ?

I presume these are both private procedures, that is, neither appears in the package specification.
If both procedures appeared in the spec there would be no problem, because the body compiles against the public declaration.
Likewise if one procedure appeared in the spec then again there would be no problem: simply write the private procedure in the body before the public procedure.
But if both procedures are private, then you need to use forward declaration. This simply means declaring the procedures' signatures at the top of the package. Exactly like putting them in the spec, only private. Here is a spec ...
create or replace package pkg as
procedure main (n0 in out number);
end;
/
... and body ...
create or replace package body pkg as
-- forward declarations
procedure p1 (n1 in out number);
procedure p2 (n2 in out number);
-- actual declarations
procedure p1 (n1 in out number)
is
begin
dbms_output.put_line('P1='||n1);
if n1 < 3 then
p2(n1);
end if;
end p1;
procedure p2 (n2 in out number)
is
begin
dbms_output.put_line('P2='||n2);
n2 := n2+1;
if n2 < 3 then
p1(n2);
end if;
end p2;
procedure main (n0 in out number)
is
begin
if n0 < 0 then
p1(n0);
else
p2(n0);
end if;
end main;
end pkg;
/
"I need to have the interdependent procedures"
You really should try to figure out a way to avoid cyclic dependencies. It is a very bad programming practice. It is hard to understand how the procedures interrelate, and we have to manage the recursion and ensure that both procedures will definitely exit eventually without calling the other. Remember, this is not just a problem you have: it's a legacy you will pass one to future maintainers of your code.

You can break the dependency chain by using packages and compiling all your package specs prior to compiling package bodies. The specs will all compile without regard to dependencies and then when the bodies are compiled they can do so in any order as they will reference the already compiled specifications. Typically, you'll have package specs stored with a *.pks suffix and bodies with *.pkb suffix and some sort of build script with compiles them. Like so
/* Master Package Build Script */
-- Specs
#package_a.pks
#package_b.pks
#package_c.pks
-- Bodies
#package_a.pkb
#package_b.pkb
#package_c.pkb

If both procedures are in the specification, simply compiling the package should work:
alter package your_package compile;
This assumes that your package is valid, of course.
If either (or both) procedure is not in the specification, the best thing to do is list it as a forward declaration at the beginning of the package body.

Related

Can we declare and use a cursor in a package specification without a body?

It's said in database PL/SQL language reference in topic 10.1 What is a package? that:
If the public items include cursors or subprograms, then the package must also have a body. The body must define queries for public cursors and code for public subprograms.
I've tested cursor package spec without body and it worked fine (in DB version 19c):
create or replace package some_pak is
cursor c is select * from employees where employee_id <102;
end;
begin
for i in some_pak.c
loop
dbms_output.put_line(i.employee_id|| ' '||i.first_name||' '|| i.salary);
end loop;
end;
Result:
100 Steven 24000
101 Neena 17000
What am I doing or understanding wrong?
I am preparing for 1z0-149 exam and want to know accurate information.
It is a bit misleading, but if you take the previous paragraph from the documentation into account:
A package always has a specification, which declares the public items that can be referenced from outside the package.
... and stress the declares in that, then it sort of makes sense; if you only declare a cursor, rather than declaring and defining as your example does, then you do need a body too. For example, your specification could do:
create or replace package some_pak is
cursor c return dual%rowtype;
end;
/
But if you tried to reference that cursor you'd get "ORA-04067: not executed, package body ... does not exist".
You would then need to define the cursor in the package body, e.g.:
create or replace package body some_pak is
cursor c return dual%rowtype is
select * from dual;
end;
/
Notice that you need to declare the return type, in both the specification and body. In your original version that isn't necessary, but here the declaration still has to tell callers the structure of the data the cursor will return. That is the API contract, if you like.
Declaring and defining the cursor separately means you can redefine the cursor by recompiling just the package body (as long as the projection remains the same, of course), avoiding invalidating anything that refers to it - as would happen if you recompiled the specification.
db<>fiddle
Splitting the declaration and definition would also allow you to hide (albeit not very securely) the actual cursor query by wrapping the package body, without wrapping the specification. Whether that would ever be useful is another matter, but thought it might be worth mentioning anyway.

Oracle procedure CALL with package's constant

Let's assume simple scenario like below:
CALL DBMS_XMLSCHEMA.DELETESCHEMA(
schemaurl => 'non_existing_schemaurl',
delete_option => DBMS_XMLSCHEMA.DELETE_INVALIDATE);
-- ORA-06553: PLS-221: 'DELETE_INVALIDATE' is not a procedure or is undefined
Meanwhile when providing integer value or running inside PL/SQL block the error does not occur:
-- 1)
CALL DBMS_XMLSCHEMA.DELETESCHEMA(
schemaurl => 'non_existing_schemaurl',
delete_option => 2);
-- 2)
BEGIN
DBMS_XMLSCHEMA.DELETESCHEMA(
schemaurl => 'non_existing_schemaurl',
delete_option => DBMS_XMLSCHEMA.DELETE_INVALIDATE);
END;
/
db<>fiddle demo
Is it possible to use DBMS_XMLSCHEMA.<constant> combined with CALL(searching for credible/official source)?
According to the SQL Language Reference:
Use the CALL statement to execute a routine (a standalone procedure or
function, or a procedure or function defined within a type or package)
from within SQL.
The key is the last two words - within SQL. It's easy to think of CALL as similar to the SQL*Plus command EXEC, but CALL runs in a SQL context while EXEC runs in a PL/SQL context.
PL/SQL package variables cannot be used in SQL statements (except for PL/SQL WITH functions, but that feature doesn't work in this context). Unfortunately I can't find an authoritative source for that claim (which is why this is a Wiki answer; hopefully someone else can add that source and then remove this sentence.)
The way you are trying to call is possible only in any named or unnamed block of pl/sql.
To call it independently, you need to pass the arguments only.
CALL DBMS_XMLSCHEMA.DELETESCHEMA('none_existing_schemaurl','DELETE_INVALIDATE');

How do I properly call DBMS_WM.GOTOWORKSPACE inside a procedure

I apologize for this relatively simple question but I am new to Oracle and DBMS Workspace Manager. I have a stored procedure that checks the current workspace and if it differs from the workspace being passed in it will execute the DBMS_WM.GOTOWORKSPACE. All the examples show using EXEC while calling DBMS_WM.GOTOWORKSPACE but when I have the following syntax inside TOAD it says its invalid, if I take the EXEC off no errors shown.
Which is correct?
//Toad Does not like this synatx
IF UPPER(l_current_workspace) != UPPER(i_workspace) THEN
EXEC DBMS_WM.GOTOWORKSPACE (i_workspace);
END IF;
OR
//TOAD is fine with this
IF UPPER(l_current_workspace) != UPPER(i_workspace) THEN
DBMS_WM.GOTOWORKSPACE (i_workspace);
END IF;
I would guess that the examples are in a form something like EXEC DBMS_WM.GOTOWORKSPACE (<>);and not between a conditionalIF...END IF;`
EXEC is not a PLSQL reserved word. It is a macro/shortcut in the client rather than the database server, that expands to wrap the statement it precedes into a BEGIN ... END; block.
It is used very widely on the command-line (SQLPlus and SQLcl) and editors like Toad and SQLDeveloper et al. often support it, but it is supported as a monolithic command only (EXEC as a prelude to the block) rather than inside of the block (Nested EXEC is not supported, nor is an EXEC inside of another BEGIN...END; block).
Since it isn't a reserved word, expanding it when it is in another block is a problem. Parsing it becomes ambiguous -- one could define one's own function called EXEC and it would be perfectly valid to run in a PLSQL block. The editor would then need to make an uncertain call about what to do when it encountered the word. The below example would break if EXEC were expanded instead of being recognized as a procedure in the PLSQL block:
CREATE PROCEDURE EXEC(PARAM_1 IN VARCHAR2)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE(UTL_LMS.FORMAT_MESSAGE('Param is:[%s]',PARAM_1));
END EXEC;
/
BEGIN
EXEC('VOLTRON');
END;
/
So the second example is correct (presuming it is housed in another BEGIN...END; block), and is universally compatible since it doesn't need to be intercepted and modified client-side.

Oracle out parameter not being NULL'd

My understanding is OUT formal parameters should always be defaulted to NULL when the procedure is called.
create or replace package parameter_tests as
procedure callerproc;
end parameter_tests;
/
create or replace package body parameter_tests as
procedure getstring(p_str out varchar) is
begin
if p_str is null then
dbms_output.put_line('parameter null');
else
dbms_output.put_line('parameter NOT null');
end if;
p_str :='zz';
end getstring;
procedure getcursor(p_out out sys_refcursor) is
begin
if p_out%isopen then
dbms_output.put_line('cursor open');
else
dbms_output.put_line('cursor closed');
end if;
open p_out for
select *
from dual;
end getcursor;
procedure callerproc is
lv_cursor sys_refcursor;
lv_string varchar2(2) := null;
begin
for i in 1..2 loop
getstring(lv_string);
getcursor(lv_cursor);
end loop;
end callerproc;
end parameter_tests;
/
set serveroutput on
begin
parameter_tests.CALLERPROC;
end;
/
parameter_tests.getstring would expect to output "Parameter null" both times around the callerproc loop. And when you run the code that's exactly what happens.
However the output from parameter_tests.getcursor indicates the reference cursor is still open the second time around the loop.
If the formal parameter p_out was being nulled when getcursor was called, we'd expect it to close the reference cursor. Instead it passes the open reference cursor and it's actually the OPEN FOR that tidies up the open reference cursor (and prevents us from hitting the maximum open cursors if you loop hundreds of times).
If I manually null the reference cursor between calls it does behave like we would expect.
Can anyone tell me why reference cursors are being handled as a special case when they are OUT parameters? And also what other types are handled differently?
The database version is 11.2.0.2.0.
According to the documentation:
When declaring a cursor variable as the formal parameter of a subprogram:
If the subprogram opens or assigns a value to the cursor variable, then the parameter mode must be IN OUT.
If the subprogram only fetches from, or closes, the cursor variable, then the parameter mode can be either IN or IN OUT.
The cursor parameter seems to be treated as if it had been declared as IN OUT, even though you only actually declare it as OUT. The behaviour is what you would expect to see from IN OUT; you can even fetch the cursor in the second call, and see the dummy value from dual.
It sort of makes sense given the nature of the ref cursor as a pointer, but you would think that rule would be enforced by the compiler erroring if only OUT was specified (and it isn't even reported as a warning). The example in the docs works fine with just OUT too, incidentally.
So really this looks like a compiler bug since it doesn't report the incorrect parameter direction; but also (more tenuously!) a bug in your code because it isn't declared as IN OUT. And, possibly, a further bug because you don't explicitly close the cursor - which does appear to 'fix' the issue too, sort of:
procedure callerproc is
lv_cursor sys_refcursor;
lv_string varchar2(20) := null;
begin
for i in 1..2 loop
getstring(lv_string);
getcursor(lv_cursor);
close lv_cursor;
end loop;
end callerproc;
which is probably more correct than assigning null to the cursor variable, which you mentioned also works.

PL/SQL, how to put common code lines of packages in a shared package

I have several packages which some of their Specification and body lines are the same, so I would like to put all the same codes in a Common package and share that package in all.
For procedures body, it was not so difficualt as I defined a Main procedure in my Common package and I used it in other packages, but how can I do the same for Specifications ?
suppose I have some common variable or constant variable:
CNST_S_DATA_MINIMA constant varchar2 (10) := '1900-01-01';
CNST_D_DATA_MINIMA constant date := To_date (CNST_S_DATA_MINIMA, CNST_S_FORMATO_DATE);
ERR__VOID_PARAMETER EXCEPTION;
Simply put them into the package specification (not body) of a package that is used as the container for such global constants.

Resources