Debug statements in plsql code - oracle

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.

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

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.

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.

Nested PIPELINED function is skipped by Oracle

I'll post a trivial example, which actually works, just to get an approximate picture of what I'm trying to achieve:
Here is the 'inner' function which takes the data from some table, called test_tab:
create or replace function test_inner RETURN num_typ PIPELINED
IS
BEGIN
FOR cur in (
SELECT x FROM test_tab
)
LOOP
PIPE ROW(cur.x);
END LOOP;
END;
/
Here is the 'outer' function which uses the result of inner function and transforms them appropriately:
create or replace function test_outer RETURN num_typ PIPELINED
IS
BEGIN
FOR x IN (
SELECT * FROM table(test_inner())
)
LOOP
PIPE ROW(x.column_value * 2);
END LOOP;
END;
/
And here is how I use it:
begin
execute immediate 'insert into test_tab(x) values(1)';
execute immediate 'insert into test_tab(x) values(2)';
execute immediate 'insert into test_tab(x) values(3)';
FOR x IN (
select * from table(test_outer())
) LOOP
DBMS_OUTPUT.put_line(x.column_value);
END LOOP;
end;
/
The problem is that test_inner function seems to be ignored by Oracle. When it is called separately, it 'sees' the data inserted prior to its execution. But when it's called as a part of test_outer it doesn't return any data, or maybe doesn't get called at all.
Like I said, the above example will work. But my case is a little bit more complex, so I can't post it entirely.
The test case you have posted does not reproduce the problem you claim to be seeing. So it is not a test case.
#AlexPoole has provided you with some other ideas. Basically there is nothing further we can do unless you can post a better test case, given that you cannot post the entire things (and we don't want to go through hundreds of lines of somebody else's shonky code - I for one get enough of that debugging my own stuff).
Producing a poor test case has not been a waste of time. At least you can rule out certain things: it's nothing to do with one pipelined function calling another. So it's clearly something in your specific implementation. Perhaps not in the internal function logic, but how they are called, the data they work with or some other part of the infrastructure.
All you can do is analyse the individual parts. Start with the core component, and build out, adding other components until you find the bit which breaks. Yes this is a tedious chore. But from your comments it sounds like you're quite a way through this process already.
I'm marking this CW as it isn't an answer to the question, just an extended comment.
Your both pipelined functions in fact are syntactic wrong: you are missing RETURN
See docs

Is it best practice for functions to return after raising an error in PL/SQL

I have a bunch of functions in packages that first check the validity of the work being requested and throw exceptions if not.
Example:
package body foo as
function implode (
i_foo_id number
) return implode_id as
not_implodable exception;
implode_id number;
begin
if not is_implodable(i_foo_id) then
raise not_implodable;
end if;
//Implode logic here.
return implode_id;
exception
when not_implodable then
raise_application_error(-20005, 'Imploding is not possible on this foo.');
end implode;
end foo;
I have turned on warning reporting and get something like this when I compile.
Warning(67,3): PLW-05005: subprogram IMPLODE returns without value at line 14.
If I put a return statement after the raise_application_error then the warning goes away. Since raising the error exits the call stack, is there any good reason to return null anyways?
No. It is not a best practice for functions to return after raising and error in PL/SQL.
It is possible and sometimes even recommended to add code just to shut up the compiler, like:
exception
when fooex then
raise_application_error(-20100, 'invalid number');
return null; -- silence PLW-05005
end;
Be sure to document such a peculiar code !
Generally I don't like a code that is just there to satisfy compiler warning, but there is always exceptions to this rule. In this particular case I'd say the problem is more the compiler than the code. In my opinion code like below is perfectly valid and compiler should not complain about it. In fact I think compiler should warn if a block has statements after raise as it's effectively a dead-code (unreachable code).
Let's consider the following function:
$ cat foo.sql
create or replace function foo(p_in in number)
return number is
fooex exception;
begin
if p_in < 1 then
raise fooex;
end if;
return p_in;
exception
when fooex then
raise_application_error(-20100, 'invalid number');
end;
/
show errors
Running this in Oracle 11g XE:
-- By default the warnings are disabled
SQL> #foo
Function created.
No errors.
Usually I want all warnings:
SQL> alter session set plsql_warnings = 'ENABLE:ALL';
Session altered.
SQL> #foo
SP2-0806: Function created with compilation warnings
Errors for FUNCTION FOO:
LINE/COL ERROR
-------- -----------------------------------------------------------------
1/1 PLW-05005: subprogram FOO returns without value at line 13
1/1 PLW-05018: unit FOO omitted optional AUTHID clause; default
value DEFINER used
After reviewing the code I found out the warnings above are incorrect, so I disable them for this compilation unit only:
SQL> alter session set plsql_warnings = 'ENABLE:ALL, DISABLE:(5005,5018)';
Session altered.
SQL> #foo
Function created.
No errors.
SQL>
Now all is good and great.
My recommendation is to turn on all warnings by default and then turn off wrong positives per compilation unit (alter session set plsql_warnings = 'ENABLE:ALL, DISABLE:(5005,5018)';). If possible: alter system plsql_warnings = 'ENABLE:ALL'; but in practice this might be a bit too harsh ...
And oh, it is a recommended practice to turn on all compiler error checks. We as a programmers need all the help the compilers can give to us ! Unfortunately with Oracle PL/SQL compiler this needs to be compromised.
Best practice is to always raise any exceptions that your function (or procedure, as the case may be) doesn't know how to handle.
For example, if your function queries a table, and the logic is "if no rows are found, return 0" or something, then I'd expect your function to handle the exception and return a sane value.
If your function gets an unexpected exception, e.g. VALUE_ERROR (e.g. due to bad data in the table caused by some bug in a data load procedure, or a missing constraint), that's arguably not the responsibility of the function; I'd expect your function to propagate the exception to the caller (although I might write the function to at least log the error before re-raising the exception).
This is why you should always have a RAISE; in any WHEN OTHERS exception handler.
The only time I'd make an exception to this rule is if I'm coding an API for some legacy system which can't handle exceptions, and expects an "error flag" (e.g. as an out parameter). In that case, I might use a WHEN OTHERS THEN exception handler to log the unexpected error, and return the error flag to the caller. It's not good coding style though, because it puts a lot of trust in the calling code to check the "error flag" before assuming the function did its work without error.
It doesn't matter one way or the other. Adding a return will make the whatever code validator your using happy. The statement will never get reached as raising the application error puts control in caller's exception handler if exists or execution ends.
Personally, I code according the syntax of the language and hope the validators eventually correct their mistakes.

Resources