oracle plsql: retrieve runtime parameter values when you call a procedure - oracle

I need a generalized method to get list of runtime parameters (values) when I call a procedure. I need something similar to the $$PLSQL_UNIT that returns the name of the running procedure.
(plsql Oracle 10g)
E.g. look at this sample procedure:
(it simply prints its own name and parameters )
CREATE OR REPLACE PROCEDURE MY_PROC(ow in varchar2, tn IN varchar2)
IS
BEGIN
dbms_output.put_line('proc_name: '||$$PLSQL_UNIT||' parameters: '|| ow||' '||tn );
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ERRORE: ' ||SQLERRM);
END MY_PROC;
/
Running procedure produces the following output:
SQL>
1 BEGIN
2 IBAD_OWN.MY_PROC('first_par', 'second_par');
3 END;
4 /
proc_name: MY_PROC parameters: first_par second_par
PL/SQL procedure successfully completed.
I'm not satisfy because I can't copy and paste in all my procedures because I have to hard code each procedure to set their right parameter variables.
Thanks in advance for the help.

It isn't possible to dynamically retrieve the values of parameters passed to a procedure in Oracle PL/SQL. The language simply isn't designed to handle this kind of operation.
Incidentally, in a procedure that is located within a package, $$PLSQL_UNIT will only return the package's name. I find it's better to define a consistently-named constant within each procedure that contains the procedure's name.

When I wanted the same functionality as yours I didn't find any good built-in solution.
What I did is: wrote DB-level trigger which modifies original body of function/procedure/package.
This trigger adds immediatly after "begin" dynamically generated piece of code from "user_arguments".
Plus, after that I include into this trigger the code, that logs calls of procs when exception occures.
Plus, you can trace procs calls, and many more interisting things.
But this solution works fine only for preproduction because performance decreases dramatically.
PS. Sorry for my bad English.

Related

What to use for input parameter when debugging Oracle Stored Procedure using TOAD debugger

I inherited an awesome 600 line Stored Procedure in which I need to debug. I'm trying to debug by right clicking on the name of the stored procedure, clicking execute using sql debugger. This brings up a table in which I can manually enter my parameters. Great. Except one of the parameters is a OracleArray vArray INPUT parameter, and I'm not sure how to actually enter something like this as a parameter? I'm not familiar with Oracle.
Your question doesn't have a lot of details, so I'll have to give a vague answer. Here's how you can call a procedure (named other_procedure) which takes a VARRAY argument or other complicated setup. Once you've declared a test procedure, you can execute it with the debugger and then step into the procedure you actually care about.
CREATE OR REPLACE PROCEDURE test_procedure IS
-- (size and type of the varray should match the one in other_procedure)
TYPE my_array_t IS VARRAY(4) OF VARCHAR2(20);
-- define and initialize your array
test_array my_array_t := my_array_t('one','two','three');
BEGIN
-- call the procedure
other_procedure(test_array);
END;
/
If you're still having trouble, edit your question to provide more details (like the procedure definition and varray definition) and we can give a more specific answer.

Calling another PL/SQL procedure within a procedure

I'm new to PL/SQL & would greatly appreciate help in this. I've created a procedure to copy contracts. Now I want to call another procedure from within this procedure which shall copy all the programs related to the contract I'm copying. One contract can have multiple programs.
You cal call another procedure in another package by using PackageName.ProcedureName(vcParameters => 'InputParameter1'); If the procedure is in the same package you could do it without the PackageName, so just ProcedureName(vcParameters => 'InputParameter1');
You call a procedure by simply putting its name and parameters in your code, e.g.
begin
dbms_output.put_line('Demo');
end;
or within a procedure,
create or replace procedure demo
as
begin
dbms_output.put_line('Demo');
end;
I have used dbms_output.put_line as an example of a procedure, but obviously any other procedure would be called the same way:
begin
foo;
bar(1);
demo(true, 'Bananas', date '2018-01-01');
end;
For some reason, many beginners are tempted to add exec before the procedure call. I don't know where that comes from because PL/SQL has no such keyword. Possibly they are thinking of the SQL*Plus execute command, which can be abbreviated to exec. However, SQL*Plus is a separate command line utility with its own commands that have nothing to do with the PL/SQL language.

Using a pl/sql procedure to log errors and handle exceptions

so far stack overflow and the oracle forums and docs have been my best friend in learning PLSQL. I'm running into an issue here. Any advice is appreciated. I'm writing a procedure that would be used to log any errors a package may encounter and log them into the error log table I created. here is my code thus far.
CREATE OR REPLACE PROCEDURE APMS.test_procedure AS
procedure write_error_log (errcode number, errstr varchar2, errline varchar2) is
pragma autonomous_transaction;
-- this procedure stays in its own new private transaction
begin
INSERT INTO error_log
(ora_err_tmsp,
ora_err_number,
ora_err_msg,
ora_err_line_no)
values (CURRENT_TIMESTAMP,
errcode,
errstr,
errline);
COMMIT; -- this commit does not interfere with the caller's transaction.
end write_error_log;
BEGIN
INSERT INTO mockdata
VALUES ('data1', 'mockname', 'mockcity');
exception when others then
write_error_log(sqlcode,sqlerrm,dbms_utility.format_error_backtrace);
raise;
END test_procedure;
/
In the procedure I currently am using a mockdata table to induce an invalid number error and log that to the error_log table. At this point the error log table proves to be functional and inserts the data needed. The next step for me is to use this procedure to be used in the exception handlers in other programs so that the error is caught and logged to the table. Currently, my procedure is only unique to the mock_data table. My mentor/superior is telling me I need to pass this program some parameters to use it in other packages and exception handlers. I'm just having a bit of trouble. Any help would be appreciated thank you!
Steven Feuerstein has written several articles in Oracle Magazine on how to handle errors in PLSQL. He offers a small framework (errpkg) for doing this. The DRY principle (Don't Repeat Yourself) is his mantra!
https://resources.oreilly.com/examples/0636920024859/blob/master/errpkg.pkg
First, you should not make your caller pass in errline. That's very tedious! And what happens when the developer needs to insert a line or two of code? Do they need to update every call to write_error_log after that point to update the line numbers? Your write_error_log should use dbms_utility.format_call_stack (or the 12c more convenient variant of that.. don't have its name handy) to figure out what line of code issued the call to write_error_log.
Then, you should have a 2nd procedure called, say, write_exception. All that needs to do is something like this:
write_error_log (SQLCODE, SUBSTR (DBMS_UTILITY.format_error_stack || DBMS_UTILITY.format_error_backtrace, 1, 4000));

PL/SQL Who am I function similar to T-SQL's OBJECT_NAME(##PROCID)

In T-SQL, the following command will return the name of the current running stored procedure:
OBJECT_NAME(##PROCID)
In PL/SQL, when I place the following code on a stored procedure of a package, it returns the name of the parent package rather than the executing stored procedure.
$$PLSQL_UNIT
Is there a way to get the name of the executing proceure in PL/SQL?
Yes, it's impossible in versions prior to 12. However you can try some hacks.
V$SESSION view has PLSQL_ENTRY_SUBPROGRAM_ID and PLSQL_SUBPROGRAM_ID fields which can lead you to currently executing procedure.
Current session is:
select PLSQL_ENTRY_OBJECT_ID,
PLSQL_ENTRY_SUBPROGRAM_ID,
PLSQL_OBJECT_ID,
PLSQL_SUBPROGRAM_ID
from V$SESSION
where AUDSID = sys_context( 'userenv', 'sessionid' )
And then find a procedure name by querying a view ALL_PROCEDURES:
select PROCEDURE_NAME
from ALL_PROCEDURES
where OBJECT_ID = :objectId
and SUBPROGRAM_ID = :subprogramId
This view contains functions and procedures declared in packages but doesn't contain those which are declared in package bodies.
dbms_utility.format_call_stack shows line number and source name. Parsed output can be gained by owa_util.who_called_me. The raw output also contains object handle which can give you access to source code of anonymous block.
dbms_utility.format_call_stack sample output:
----- PL/SQL Call Stack -----
object line object
handle number name
B87FEF1C 1 anonymous block
And then:
select SQL_FULLTEXT from V$SQL where CHILD_ADDRESS = 'B87FEF1C'
The source code of stored procedures can be gained from ALL_SOURCE.
Once you have a source code of calling method and a line number in that code, you can parse it to obtain procedure name.
The only bad example is one-liners. But it's a rare situation.
procedure outer is procedure inner is begin whoami; end; begin whoami; end;
You have information that whoami was called on this line. But you don't know if it's the first occurrence or the second one. So, even parsing the source code cannot lead you to the exact solution.
Other situations can be processed by parsing the source code. Here's my attempt. Sample usage:
create package APCKG is
procedure PROC;
end;
/
create package body APCKG is
procedure PROC is
procedure "INNER/proc" is
begin
dbms_output.put_line( p_stack.whoAmI );
end;
begin
"INNER/proc";
end;
end;
/
begin
APCKG.PROC;
end;
Output:
5: YOUR_SCHEMA.PACKAGE BODY APCKG.PROCEDURE PROC.PROCEDURE "INNER/proc"
Output format:
Line number + ': ' + Owner + [ '.' + Type + ' ' + Name ]*
So it returns only the last caller and its hierarchy. One procedure inside another one which is inside a function which is located in a package body.
Use utl_call_stack if you need an exact solution and have Oracle 12. This solution is for prior versions (tested on 10.2). It will return the first occurrence for one-liner example above.
UPD:
I've upgraded this solution to a full-scale package for call stack control.
Benefits:
Tested on Oracle 9 (separate package version), 10 and 11.
It really parses the source (i.e. lexical analysis, tokenization etc.).
Pure PL/SQL.
Parses anonymous blocks source code (they can contain inner procedures too).
Contains methods similar to utl_call_stack.
Methods whoAmI and whoCalledMe.
Supports double quoted names.
Supports q-notation for strings.
Skips multiline and one-line comments.
Skips procedure and function declarations without definitions.
Source code.
UPD 2:
Added support for conditional compilation.
UPD 3:
Implemented a backport of Oracle 12 package utl_call_stack for versions 9, 10 and 11.
just define a constant with the name of the procedure in the DECLARE section
whoami CONSTANT VARCHAR2(100) := 'MY_PROCEDURE';

How do I pass a cursor value when calling a stored procedure?

This is only my second time diagnosing a PL/SQL procedure. I need to test the code in the stored procedure, and I'm trying to call it in SQL Developer. I have run the error details report, and the code has no obvious bugs in it.
So now I am trying to run it through a test window so I can see if the output is correct. However I can't seem to get the right argument for the 3 parameter. Here are the parameters in the the procedure.
CREATE OR REPLACE PROCEDURE ADVANCE.WW_DEATHDATE_REPORT(begindate varchar2, enddatevarchar2, RC1 IN OUT du_refCUR_PKG.RC) AS
Here is the Code I am trying to use to call the procedure. What do I need to do to get it to run correct? I keep getting error messages saying I'm using a wrong value in the parameter.
BEGIN
ADVANCE.WW_DEATHDATE_REPORT('20100101','20150101',du_refcur_pkg);
END;
There are multiple ways to do this, one way is like the below,
DECLARE
du_refcur_pkg SYS_REFCURSOR;
BEGIN
OPEN du_refcur_pkg FOR SELECT ... ;
ADVANCE.WW_DEATHDATE_REPORT('20100101','20150101',du_refcur_pkg);
END;
Another way would be,
BEGIN
ADVANCE.WW_DEATHDATE_REPORT( '20100101','20150101', CURSOR (SELECT ... ) );
END;

Resources