How do I properly call DBMS_WM.GOTOWORKSPACE inside a procedure - oracle

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.

Related

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');

Calling procedure

If we call a procedure inside a procedure like
procedure ter () is cursor XXXXX
another procedure load_ter()
another procedure load_value()
end ter;
Does it calls order wise inside the procedure. or it can be from anywhere
If you run procedure TER and call LOAD_TER, then LOAD_TER will execute until it finishes. Then LOAD_VALUE will be called, executed. Once it finishes, the rest of commands in TER will be run and - finally - TER will terminate as well.
If you execute this procedure:
create or replace procedure demo as
begin
dbms_output.put_line('One');
dbms_output.put_line('Two');
dbms_output.put_line('Three');
end demo;
the output will be (with dbms_output enabled):
One
Two
Three
That is, the instructions within a procedure will always executed in the order you coded them.
I have to wonder what prompted this question.

IF ELSE condition executed even though condition is never true

This is a simple sql statement used to print 2 different questions based on the choice entered. I notice that both statements are run even though the else or if condition is not true. The output printed is correct but I don't require the substitution variable to prompt user for entry if the statement is not true.
SET SERVEROUTPUT ON;
DECLARE
choice number(2) := '&Please_Enter_Choice';
question varchar2(50);
BEGIN
if choice = 1 then
question := '&Whats_your_name?';
else
if choice = 2 then
question := '&How_old_are_you?';
end if;
end if;
DBMS_OUTPUT.PUT_LINE(question);
END;
Substitution variables like &Please_Enter_Choice are replaced in a preprocessing step that takes p!ace (on the client) before the actual code is run (on the server). Please also keep in mind that support for these variables is a feature of the frontend tool (e.g. sql*plus), while the database does not even know they ever existed. Conclusion: you cannot make interactive PL/SQL scripts like that.

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.

Debug statements in plsql code

I have been working with a lot of legacy plsql code off late. I see debug statements like below all over the code.The 'if' check was added to avoid the overhead of procedure call when debug mode is OFF.
IF (Debug mode is on) THEN
Debug_pkg.logmsg(l_module_name,'Unexpected error : '|| SQLERRM, FND_LOG.LEVEL_WARNING);
END IF;
Is there a way to write debug statement in one line? In C, we have macros that can be useful to avoid procedural overhead. Can I do something similar in plsql code?
Edit:
Adding a little more details since my question might have confused some. What I am trying to avoid is writing 3 lines to print one debug statement. Can I write debug statement in one line using macros?
Reason why I am trying to do this,
When such statements are added all over the place it gives rise to clutter and reduces readability. When it should have been just one line, I see 3 lines for each debug statement.
Second edit:
Added my answer below.
Depending on the version of Oracle you're using, it is possible to use conditional compilation in PL/SQL. Something like
$IF $$debug_mode
$THEN
Debug_pkg.logmsg(l_module_name,'Unexpected error : '|| SQLERRM, FND_LOG.LEVEL_WARNING);
$END
where $debug_mode is set in the session's PLSQL_CCFLAGS setting or
$IF package_name.debug_mode = 1
$THEN
Debug_pkg.logmsg(l_module_name,'Unexpected error : '|| SQLERRM, FND_LOG.LEVEL_WARNING);
$END
where package_name.debug_mode is a package-level variable that indicates whether the code should be compiled in debug mode.
Again, depending on the version of Oracle you're using, you may be able to put the debug mode check in debug_pkg.logmsg and rely on the compiler either to automatically determine that it would be beneficial to inline the call (eliminating the procedure call overhead) or to instruct the compiler to inline the call with the INLINE pragma. Here is an article that discusses inlining in PL/SQL in more detail.
Thanks for the link http://www.oracle-developer.net/display.php?id=502 from Justin Cave. I ended up doing this for now,
PROCEDURE debug_msg(txt VARCHAR2)
IS
BEGIN
IF (debug mode is on) THEN
debug_pkg.logmsg('Module', txt , LEVEL_WARNING);
END IF;
END;
To inline the above procedure, you need to do this.
PRAGMA INLINE(debug_msg, 'YES');
However inline is just a request to the compiler, it may or may not be inlined. Also, the procedure being inlined needs to be defined in the same package.

Resources