How can a PL/SQL procedure tell if it is being run from a concurrent program? - oracle

I want to write a procedure that logs output to the Oracle concurrent manager log when run from a concurrent program, but writes to dbms_output when run "standalone".
Is there a way from PL/SQL to check whether my code is being run from a concurrent request? The best way I've been able to find is
select * from fnd_concurrent_requests
where oracle_session_id = userenv('SESSIONID');
but that's pretty slow. Is there a function or table I can query that gives me the information more efficiently?

You can best use fnd_global.conc_request_id like we do in our blitz report code:
procedure write_log(p_text in varchar2, p_log_level in number default 1) is
begin
if fnd_global.conc_request_id>0 then
fnd_file.put_line(fnd_file.log,p_text);
else
fnd_log.string(p_log_level,'XXEN',p_text); --or your dbms_output.put_line() call
end if;
end write_log;

Add a boolean flag argument to the procedure that you can use to tell it where you want to log to when you call the procedure and then pass different flags from your two different (concurrent/non-concurrent) programs:
CREATE PROCEDURE my_proc(
i_value1 IN NUMBER,
i_use_concurrent_logging IN BOOLEAN DEFAULT FALSE
)
IS
-- Helper function so you only check the flag in one place.
PROCEDURE log(value IN VARCHAR2)
IS
BEGIN
IF i_use_concurrent_logging THEN
-- put your concurrent logging code here.
NULL;
ELSE
DBMS_OUTPUT.PUT_LINE(value);
END IF;
END;
BEGIN
-- Do stuff.
log('Stuff done');
-- Do other stuff
log('Other Stuff done');
END;
/
If you want to use your check once in the procedure then you could use:
CREATE OR REPLACE PROCEDURE my_proc(
i_value1 IN NUMBER
)
IS
v_use_concurrent_logging BOOLEAN := FALSE;
PROCEDURE log(value IN VARCHAR2)
IS
BEGIN
IF v_use_concurrent_logging THEN
-- put your concurrent logging code here.
NULL;
ELSE
DBMS_OUTPUT.PUT_LINE(value);
END IF;
END;
BEGIN
DECLARE
v_exists INT;
BEGIN
SELECT 1
INTO v_exists
FROM fnd_concurrent_requests
WHERE oracle_session_id = userenv('SESSIONID')
AND ROWNUM = 1;
v_use_concurrent_logging := TRUE;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_use_concurrent_logging := FALSE;
END;
-- Do stuff.
log('Stuff done');
-- Do other stuff
log('Other Stuff done');
END;
/
db<>fiddle here

Related

PL/SQL "all_search" procedure is not a procedure or is undefined

This function is inside a package, but when I call the function the following error appears: PL/SQL "all_search" is not a procedure or is undefined. Someone can help me?
CREATE OR REPLACE PACKAGE employee_tab IS
FUNCTION all_search (ID_EMP in NUMBER) RETURN O_T_EMPL PIPELINED;
END employee_tab;
/
CREATE OR REPLACE TYPE O_T_EMPL AS TABLE OF O_EMPLOYEE;
/
CREATE OR REPLACE PACKAGE BODY employee_tab IS
FUNCTION all_search (ID_EMP in NUMBER) RETURN O_T_EMPL PIPELINED
IS
TAB_OBJC_EMP O_T_EMPL;
MY_QUERY_SEARCH VARCHAR2(400);
REF_C SYS_REFCURSOR;
MAX_ROW NUMBER := 25;
BEGIN
MY_QUERY_SEARCH := 'SELECT *
FROM EMPLOYEES
WHERE EMPLOYEE_ID = ID_EMP';
open REF_C for MY_QUERY_SEARCH using ID_EMP;
loop
--
fetch REF_C bulk collect into TAB_OBJC_EMP limit MAX_ROW;
exit when TAB_OBJC_EMP.count = 0;
for i in 1..TAB_OBJC_SEE.count
--
loop
pipe row(O_EMPLOYEE(TAB_OBJC_EMP(i).V_O_EMP_ID,
TAB_OBJC_EMP(i).V_O_HIRE_ID,
TAB_OBJC_EMP(i).V_O_DEP_ID)
);
end loop;
--
END loop;
--
CLOSE REF_C;
RETURN;
--
END all_search;
END employee_tab;
/
call function: employee_tab.all_search(1);
You need to assign the result of the function to something, try something like:
DECLARE
l_emp_id NUMBER;
BEGIN
SELECT EMP_ID
INTO l_emp_id
FROM TABLE(employee_tab.all_search(1))
WHERE rownum = 1;
DBMS_OUTPUT.put_line(TO_CHAR(EMP_ID));
END;
/

Ignore lines that causes errors

I have a big Oracle script with thousands of package call inside a BEGIN - END;
Is there a way to ignore the lines that causes error and continue executing the next lines? Some sort of "On Error Resume Next" in vb.
If you have only one BEGIN END section, then you can use EXCEPTION WHEN OTHERS THEN NULL.
SQL> declare
v_var pls_integer;
begin
select 1 into v_var from dual;
-- now error
select 'A' into v_var from dual;
exception when others then null;
end;
SQL> /
PL/SQL procedure successfully completed.
SQL> declare
v_var pls_integer;
begin
select 1 into v_var from dual;
-- now error
select 'A' into v_var from dual;
--exception when others then null;
end;
/
declare
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at line 6
SQL>
The whole concept of "ignore errors" is a bug, and a lie if any errors occur. That is not to say you cannot trap errors and continue processing, just that you MUST handle the errors. For example, assume the use case: "Data has been loaded into a stage table from multiple .csv files. Now load into the tables A and Table B according to ....".
create procedure
Load_Tables_A_B_from_Stage(process_message out varchar2)
is
Begin
For rec in (select * from stage)
loop
begin
insert into table_a (col1, col2)
values (rec.col_a1, col_a2);
insert into table_b (col1, col2)
values (rec.col_b1, col_b2);
exception
when others then null;
end;
end loop;
process_message := 'Load Tables A,B Complete';
end ;
Now suppose a user created the a .csv file entered "n/a" in numeric columns where there was no value or the value was unknown. The result of this all too common occurrence is all such rows were not loaded, but you have no way to know that until the user complains their data was not loaded even though you told them it was. Further you have no way of determining the problem.
A much better approach is to "capture and report".
create procedure
Load_Tables_A_B_from_Stage(process_message out varchar2)
is
load_error_occurred boolean := False;
Begin
For rec in (select * from stage)
loop
begin
insert into table_a (col1, col2)
values (rec.col_a1, rec.col_a2);
exception
when others then
log_load_error('Load_Tables_A_B_from_Stage', stage_id, sqlerrm);
load_error_occurred := True;
end;
begin
insert into table_b (col1, col2)
values (rec.col_b1, rec.col_b2);
exception
when others then
log_load_error('Load_Tables_A_B_from_Stage', stage_id, sqlerrm);
load_error_occurred := True;
end;
end loop;
if load_error_occurred then
process_message := 'Load Tables A,B Complete: Error(s) Detected';
else
process_message := 'Load Tables A,B Complete: Successful No Error(s)';
end if;
end Load_Tables_A_B_from_Stage ;
Now you have informed the user of the actual status, and where you are contacted you can readily identify the issue.
User here is used in the most general sense. It could mean a calling routine instead of an individual. Point is you do not have to terminate your process due to errors but DO NOT ignore them.
I don't think there is any magic one-liner that will solve this.
As others have, use a editor to automate the wrapping of each call within a BEGIN-EXCEPTION-END block might be quicker/easier.
But, if feel a little adventurous, or try this strategy:
Let's assume you have this:
BEGIN
proc1;
proc2;
proc3;
.
.
.
proc1000;
END;
You could try this (untested, uncompiled but might give you an idea of what to try):
DECLARE
l_progress NUMBER := 0;
l_proc_no NUMBER := 0;
e_proc_err EXCEPTION;
-- A 'runner' procedure than manegrs the counters and runs/skips dpending on these vals
PROCEDURE run_proc ( pname IN VARCHAR2 ) IS
BEGIN
l_proc_no := l_proc_no + 1;
IF l_proc_no >= l_progress
THEN
-- log 'Running pname'
EXECUTE IMMEDIATE 'BEGIN ' || pname || '; END;' ;
l_progress := l_progress + 1;
ELSE
-- log 'Skipping pname'
END IF;
EXCEPTION
WHEN OTHERS THEN
-- log 'Error in pname'
l_progress := l_progress + 1;
RAISE e_proc_err;
END;
BEGIN
l_progress := 0;
<<start>>
l_proc_no := 0;
run_proc ( 'proc1' );
run_proc ( 'proc2' );
run_proc ( 'proc3' );
.
.
run_proc ( 'proc1000' );
EXCEPTION
WHEN e_proc_err THEN
GOTO start;
WHEN OTHERS THEN
RAISE;
END;
The idea here is to add a 'runner' procedure to execute each procedure dynamically and log the run, skip, error.
We maintain a global count of the current process number (l_proc_no) and overall count of steps executed (l_progress).
When an error occurs we log it, raise it and let it fall into the outer blocks EXCEPTION handler where it will restart via an (evil) GOTO.
The GOTO is placed such that the overall execution count is unchanged but the process number is reset to 0.
Now when the run_proc is called it sees that l_progress is greater than l_proc_no, and skips it.
Why is this better than simply wrapping a BEGIN EXCEPTION END around each call?
It might not be, but you make a smaller change to each line of code, and you standardise the logging around each call more neatly.
The danger is a potential infinite loop which is why I specify e_proc_err to denote errors within the called procedures. But it might need tweaking to make it robust.

Executing functions in sequence defined in a package

I've been given the task of converting a stored procedure, with several CRUD operations, to a package with functions defined.
When I created the package in Oracle SQL Developer, I defined may functions:
FUNCTION func1 RETURN NUMBER;
FUNCTION func2 RETURN NUMBER;
etc...
I have corresponding code in the 'package body':
FUNCTION func1 RETURN NUMBER
IS
BEGIN
-- some CRUD operation
END;
RETURN 0;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('FUNCTION func1: error ' || ERR_NUM || ', Msg:' || ERR_MSG);
ROLLBACK;
END func1;
When I run them, I can choose which function to run from the list supplied. My question is what do I need to write or do to have all functions run in sequence with one command?
thanks
If you need to run some procedures in a given order, you need to build a procedure or an anonymous block that calls the procedures in the right order.
For example:
create or replace package aPackage is
procedure runAll;
end;
create or replace package body aPackage is
procedure proc1 is
begin
dbms_output.put_line('Procedure 1');
end proc1;
--
procedure proc2 is
begin
dbms_output.put_line('Procedure 2');
end proc2;
procedure runAll is
begin
proc1;
--
proc2;
--
end runAll;
end;
The call:
SQL> exec aPackage.runAll;
Procedure 1
Procedure 2
PL/SQL procedure successfully completed.
SQL>
I used procedures just to call them without need to assign the result to a variable, but the idea is the same for functions.
With functions:
create or replace package aPackageFun is
function runAll return number;
end;
create or replace package body aPackageFun is
function fun1 return number is
begin
dbms_output.put_line('Inside function 1');
return 1;
end fun1;
--
function fun2 return number is
begin
dbms_output.put_line('Inside function 2');
return 2;
end fun2;
function runAll return number is
vNum1 number;
vNum2 number;
begin
vNum1 := fun1();
--
vNum2 := fun2();
--
return vNum1 + vNum2;
end runAll;
end;
The call:
SQL> select aPackageFun.runAll() from dual;
APACKAGEFUN.RUNALL()
--------------------
3
Inside function 1
Inside function 2
SQL>
About your code, notice that you have a commit after a return: the commit will never be executed.

Where in stored procedure I can make exit status?

I have oracle stored procedure where i check sender I'd,source system, and transaction number at the beginning of the procedure. Can I do it this way:
If Id != "aaa"
Exit -1;
Else if source = " ".
Exit -1;
Else if trans = " ".
Exit -1;
Else.
-- continues stored procedure
I appreciate any help
To rephrase your question more generally, you want a caller of your routine to know if something bad has happened inside it. There are (at least) three ways of doing this in PL/SQL.
Use an OUT parameter
Procedure cannot return a value, the way a function does, but it can set an output parameter:
CREATE PROCEDURE inner (p_id IN VARCHAR2(10), p_res OUT NUMBER)
IS
BEGIN
p_res := 0; -- default value
IF p_id = 'aaa' THEN
p_res := -1;
RETURN;
ELSE
-- do something
END IF;
END;
Then in the caller you would have:
DECLARE res NUMBER;
...
inner('aaa', res);
IF res = -1 THEN
-- panic!
END IF;
...
Use a function
Despite your seeming aversion to functions, this might be an option.
CREATE FUNCTION inner (p_id IN VARCHAR2(10))
RETURN NUMBER
IS
BEGIN
IF p_id = 'aaa' THEN
RETURN -1;
END IF;
-- do something
RETURN 0;
END;
Then in the caller:
...
IF inner('aaa') = -1 THEN
-- panic!
END IF;
...
Use an exception
Similar to other programming languages, PL/SQL has exceptions:
CREATE PROCEDURE inner (p_id IN VARCHAR2(10))
IS
BEGIN
IF p_id = 'aaa' THEN
RAISE_APPLICATION_ERROR(-20000, 'ID cannot be ''aaa''');
ELSE
-- do something
END IF;
END;
and in the caller:
...
DECLARE
panic EXCEPTION; -- declare exception
PRAGMA EXCEPTION_INIT (panic, -20000); -- assign error code to exception
...
BEGIN
inner ('aaa');
EXCEPTION
WHEN panic THEN
-- proceed to panic
END;
You are using a wrong syntax both for the IF...ELSE and for the exit.
Given that you are saying you need to get a return value, you probably need a function like this, using CASE:
create or replace function testFun ( pIn1 varchar2, pIn2 varchar2) return varchar2 is
begin
case
when pIn1 is null then
return -1;
when pIn2 = ' ' then
return -2;
else
return 999;
end case;
end;

Oracle PL/SQL: Call DML procedure from a function

I have a procedure that has DML commands. the procedure accepts a variable of type out, and it returns a value.
i need call this procedure from a function.
the goal isĀ  that the function will return the value of the variable out returns the procedure.
(i need it for SSIS, but I believe that it is useful in other cases.)
During attempts, I got these errors:
ORA-14551: cannot perform a DML operation inside a query tips.
ORA-06519: active autonomous transaction detected and rolled back.
I'm looking for the right syntax to do it.
Example of a solution that works:
The procedure:
create or replace procedure MyProc(outRes OUT NUMBER)
is
begin
update some_table set some_description = 'abc';
commit;
if (some_condition) then
outRes := 666;
else
outRes := 999;
end if;
end MyProc;
Note: You must do commit; at the end of the DML command.
The function:
CREATE or replace FUNCTION MyFunc
RETURN int IS
PRAGMA AUTONOMOUS_TRANSACTION;
myVar number;
begin
MyProc(myVar);
return myVar;
END MyFunc;
Note that at the beginning of the function has: PRAGMA AUTONOMOUS_TRANSACTION;
And this function call:
select MyFunc() as res from dual;
Here is an example of what you need to do. Do know this is UNTESTED, but should give you a general idea of which way to go. This is known as Dynamic SQL and uses bind variables. There's a lot more I don't know such as the data type your procedure spits out and what not... so if it's not varchar2 then change it accordingly...
FUNCTION myFunc(procedure_call varchar2) RETURN VARCHAR2
IS
v_out1 varchar2(500);
BEGIN
EXECUTE IMMEDIATE 'begin '||procedure_call||'( :out1 ); end;' using v_out1;
RETURN v_out;
END;

Resources