passing cursor to a function does not work always - oracle

I have the below procedure and the functions, I pass the cursor result as an argument to the function load_in_parallel. The issue is that sometimes the entries are processed and other time it doesn't. I really don't understand why it happens. Could it be due to the size of the result? Please see the PL/SQL procedure and function as below,
procedure COPY_REPORT_CONTENTS is
begin
begin
-- START PARALLEL processing from here
for rec in (
select /*+ parallel(5) */ * from table(load_in_parallel(CURSOR(
select gen_report_id, creation_ts, selection_criteria, content from stage_table stgrep where not exists(select 1 from content rep where rep.report_id = stgrep.gen_report_id) and nvl(stgrep.gen_report_id, 0) <> 0)))
)
LOOP
--some processing here
END LOOP;
exception
when others then
ROLLBACK;
raise;
end;
end;
function load_in_parallel (c_pis_a1 IN SYS_REFCURSOR)
return vdps_load_stats
PARALLEL_ENABLE (PARTITION c_pis_a1 BY ANY)
PIPELINED
IS
PRAGMA AUTONOMOUS_TRANSACTION;
begin
loop
begin
fetch c_pis_a1 into r_pis_a1;
exit when c_pis_a1%notfound;
--some processing
commit;
exception
when others then
rollback;
--move the records to failed table and continue with next records
commit;
end;
end loop;
close c_pis_a1;
PIPE ROW(v_vdps_load_stats);
RETURN;
end;
Any help will really be appreciated.
Note: included the missing exception block.

Related

ROLLBACK inside oracle for loop is not working for the statements mentioned outside of for loop

I have a pl sql procedure which accepts array of elements and inserts them into a table.
At the beginning of the procedure i am deleting data from backup table and inserting data from main table into backup table. Then i am deleting data from main table and looping through the arguments to the proc and inserting records. When i face dup_val_on_index exception, the rollback is happening to start point of the proc. I mean the exception block is getting executed. But the rollback is not happening.
For example, if i insert 2 rows which has duplicate values, dup_val_on_index exception has to be raised and 1st row should not be inserted.
Below is my code. If any exception happening inside the loop, i want to rollback the insert as well and delete operation performed at the beginning of the procedure
PROCEDURE insert_sales_data (
p_depot_code IN depotcode_array,
p_depot_name IN depotname_array,
p_dell_split IN dellsplit_array,
p_sector IN sector_array,
p_locality IN locality_array,
p_tnt_depot_code IN tntdepotcode_array,
p_postal_code IN postalcode_array,
p_primary_sort IN primarysort_array,
p_secondary_sort IN secondarysort_array,
p_user IN VARCHAR2,
p_error_message OUT VARCHAR2,
p_count OUT NUMBER
)
IS
BEGIN
SAVEPOINT s1;
DELETE FROM sales_backup;
INSERT INTO sales_backup
SELECT
*
FROM
sales;
DELETE FROM sales;
FOR i IN p_sector.first..p_sector.last LOOP
BEGIN
INSERT INTO sales (
depot_code,
depot_name,
dell_split,
sector,
locality,
tnt_depot_code,
postal_code,
primary_sort,
secondary_sort,
create_date,
create_user_id,
uuid
) VALUES (
p_depot_code(i),
p_depot_name(i),
p_dell_split(i),
p_sector(i),
p_locality(i),
p_tnt_depot_code(i),
p_postal_code(i),
p_primary_sort(i),
p_secondary_sort(i),
SYSDATE,
p_user,
sys_guid()
);
EXCEPTION
WHEN dup_val_on_index THEN
ROLLBACK TO s1;
EXIT;
WHEN OTHERS THEN
ROLLBACK TO s1;
EXIT;
END;
END LOOP;
SELECT
COUNT(*)
INTO p_count
FROM
uk_depots;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK TO s1;
END;
Hoping, I understood your question correctly. Please try to use below block.
PROCEDURE insert_sales_data (
p_depot_code IN depotcode_array,
p_depot_name IN depotname_array,
p_dell_split IN dellsplit_array,
p_sector IN sector_array,
p_locality IN locality_array,
p_tnt_depot_code IN tntdepotcode_array,
p_postal_code IN postalcode_array,
p_primary_sort IN primarysort_array,
p_secondary_sort IN secondarysort_array,
p_user IN VARCHAR2,
p_error_message OUT VARCHAR2,
p_count OUT NUMBER
)
IS
BEGIN
DELETE FROM sales_backup;
INSERT INTO sales_backup
SELECT
*
FROM
sales;
DELETE FROM sales;
FOR i IN p_sector.first..p_sector.last LOOP
BEGIN
INSERT INTO sales (
depot_code,
depot_name,
dell_split,
sector,
locality,
tnt_depot_code,
postal_code,
primary_sort,
secondary_sort,
create_date,
create_user_id,
uuid
) VALUES (
p_depot_code(i),
p_depot_name(i),
p_dell_split(i),
p_sector(i),
p_locality(i),
p_tnt_depot_code(i),
p_postal_code(i),
p_primary_sort(i),
p_secondary_sort(i),
SYSDATE,
p_user,
sys_guid()
);
EXCEPTION
WHEN dup_val_on_index THEN
ROLLBACK;
EXIT;
WHEN OTHERS THEN
ROLLBACK;
EXIT;
END;
END LOOP;
SELECT
COUNT(*)
INTO p_count
FROM
uk_depots;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END;
From how I understood your question it should be simpler:
PROCEDURE insert_sales_data (
p_depot_code IN depotcode_array,
p_depot_name IN depotname_array,
p_dell_split IN dellsplit_array,
p_sector IN sector_array,
p_locality IN locality_array,
p_tnt_depot_code IN tntdepotcode_array,
p_postal_code IN postalcode_array,
p_primary_sort IN primarysort_array,
p_secondary_sort IN secondarysort_array,
p_user IN VARCHAR2,
p_error_message OUT VARCHAR2,
p_count OUT NUMBER
)
IS
BEGIN
SAVEPOINT s1;
DELETE FROM sales_backup;
INSERT INTO sales_backup
SELECT
*
FROM
sales;
DELETE FROM sales;
FOR i IN p_sector.first..p_sector.last LOOP
INSERT INTO sales (
depot_code,
depot_name,
dell_split,
sector,
locality,
tnt_depot_code,
postal_code,
primary_sort,
secondary_sort,
create_date,
create_user_id,
uuid
) VALUES (
p_depot_code(i),
p_depot_name(i),
p_dell_split(i),
p_sector(i),
p_locality(i),
p_tnt_depot_code(i),
p_postal_code(i),
p_primary_sort(i),
p_secondary_sort(i),
SYSDATE,
p_user,
sys_guid()
);
END LOOP;
SELECT
COUNT(*)
INTO p_count
FROM
uk_depots;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK TO s1;
END;
This block is fairly useless, you perform the rollback no matter which error you get.
EXCEPTION
WHEN dup_val_on_index THEN
ROLLBACK TO s1;
EXIT;
WHEN OTHERS THEN
ROLLBACK TO s1;
EXIT;
END;

Commit or handle rollback in stored procedure after insertions are done?

Optimal way to handle an error in stored procedure that only makes inserts?
I have a procedure like this:
PROCEDURE INSERT_TO_TABLES (VAL1 IN NUMBER, VAL2 IN NUMBER, RESULT OUT NUMBER)
IS
BEGIN
INSERT INTO TABLE_1 (A_COLUMN) VALUES (VAL1);
INSERT INTO TABLE_2 (B_COLUMN) VALUES (VAL2);
COMMIT;
END;
How could I make this stored procedure optimal enough to handle a rollback and raise an exception to catch from the application (C# try/catch) if only 1 of those inserts throws an error? Return out value true if everything is ok or false if an error happened?
SQLERRM and SQLCODE functions may be used :
CREATE OR REPLACE PROCEDURE INSERT_TO_TABLES (VAL1 IN NUMBER, VAL2 IN NUMBER, RESULT OUT
NUMBER, RESULT_ERR OUT VARCHAR2 )
IS
v_err_code number := SQLCODE;
v_err_txt varchar2(4000) := SQLERRM;
Begin
Begin
INSERT INTO TABLE_1 (A_COLUMN) VALUES (VAL1);
INSERT INTO TABLE_2 (B_COLUMN) VALUES (VAL2);
Exception When Others then
RESULT_ERR := v_err_code||' '||v_err_txt;
End;
If ( nvl(v_err_code,0)<0 ) Then
ROLLBACK;
Else
RESULT_ERR :='Committed with success...';
COMMIT;
End If;
End;

Exception dose not working in execute immediate insert in oracle

I have some problem in execute immediate insert statement exception part.
I have a table query_tb that contains two columns (DEPT and SOURCE_VALUE)
The column contains data in below
CLERK
select a.empno,a.ename,a.job,a.mgr,a.hiredate,b.deptno,b.dname,b.loc
from emp a,dept b where a.deptno=b.deptno and a.empno= '#V_GCIF#'
SALESMAN
select e.empno,e.ename,e.job,d.deptno,d.dname,d.loc from emp e,dept
d where e.deptno=d.deptno and e.empno= '#V_GCIF#'
MANAGER
select a.empno,a.ename,a.job,b.deptno,b.dname,b.loc from employee
a,department b where a.deptno=b.deptno and a.empno= '#V_GCIF#'
ADMIN
select a.empno,a.ename,a.job,b.deptno,b.dname,b.loc from employee
a,department b where a.deptno=b.deptno and a.empno= '#V_GCIF#'
If I pass the correct empno which is keep on the emp table it runs fine. But if I pass the incorrect empno (no data) then exception part not working.
create or replace
PROCEDURE test_emp_sp(
p_id IN VARCHAR2)
AS
CURSOR rec
IS
SELECT dept,
source_value
FROM query_tb;
v_query VARCHAR2(1000);
BEGIN
FOR rec IN
(SELECT dept,source_value FROM query_tb
)
LOOP
IF rec.dept='CLERK' THEN
v_query :=REPLACE(rec.source_value,'#V_GCIF#',p_id);
EXECUTE IMMEDIATE 'INSERT INTO emp_tb (empno,ename,job,mgr,hiredate,deptno,dname,loc) ('||v_query|| ')';
dbms_output.put_line(v_query||' inserted');
ELSE
v_query:=REPLACE(rec.source_value,'#V_GCIF#',p_id);
EXECUTE IMMEDIATE 'INSERT INTO emp_tb (empno,ename,job,deptno,dname,loc) ('||v_query||')';
dbms_output.put_line(v_query||' inserting others');
END IF;
END LOOP;
commit;
EXCEPTION
WHEN others THEN
dbms_output.put_line('No data Found...');
END;
That's because you are not running a select command, it is an insert command (insert select) which means that if the select won't return rows it just doesn't insert anything and no error is thrown for that. You should check whether the insert command has affected any rows. The way you do that in Oracle is checking the SQL%ROWCOUNT immediate after the execution, if it turns to be 0 then you do your job raising an exception. It would be something like:
DECLARE
customException EXCEPTION;
PRAGMA EXCEPTION_INIT( customException, -20001 );
....
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO emp_tb (empno,ename,job,mgr,hiredate,deptno,dname,loc)
('||v_query|| ')';
IF (SQL%ROWCOUNT) = 0 then
raise_application_error( -20001, 'This is a custom error' );
end if;
EXCEPTION
WHEN customException THEN
dbms_output.put_line('No data Found...');
END;
It's a long time without programming in Oracle PLSql So somethings there on the provided code may not compile but it is all there look into it in the internet and you will be good.
Exception does not work because there is no exception.
If SELECT returns NULL then 0 rows will be INSERT.
Example:
insert into t1(c1)
select 100 from dual where 1=0;
Result: 0 rows inserted.
insert into ... select from..
will not raise a no_data_found exception. It will just insert 0 records.
To test if you have inserted any records you can use SQL%ROWCOUNT after the insert statement.
execute immedate 'INSERT...;
if SQL%ROWCOUNT=0
then
dbms_output.put_line('no records inserted');
else
...
end if;
Also you might want to consider changing #V_GCIF# into something that can be used as a bind varaiable such as :P_ID.
You can skip the replace statement and change the execute immediate into something like:
execute immediate 'INSERT INTO ...'||v_query
using p_id;
This will bind the value of p_id to the :p_id in the statement.
The very basic thing here is to capture the error appropriately so that you can able to track back. Here i have put loggers and EXCEPTION handlers jsut to check the error. Also i have put a RAISE_APPLICATION_ERROR. Did some modifications too in the snipet. Hope it helps.
PS: Have not checked for the syntax as i dont have workspace now with me.
CREATE OR REPLACE
PROCEDURE test_emp_sp(
p_id IN VARCHAR2)
AS
--Not needed
-- CURSOR rec
-- IS
-- SELECT dept,
-- source_value
-- FROM query_tb;
--Not needed
v_query VARCHAR2(1000);
BEGIN
FOR rec IN
(SELECT dept,source_value FROM query_tb
)
LOOP
IF rec.dept='CLERK' THEN
v_query :=REPLACE(rec.source_value,'#V_GCIF#',p_id);
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO emp_tb (empno,ename,job,mgr,hiredate,deptno,dname,loc) ('||v_query|| ')';
dbms_output.put_line(v_query||' inserted');
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line(' Error while inserting data in emp_tab for Clerk--> '||SQLCODE||'---'||SQLERRM);
END;
ELSE
v_query:=REPLACE(rec.source_value,'#V_GCIF#',p_id);
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO emp_tb (empno,ename,job,deptno,dname,loc) ('||v_query||')';
dbms_output.put_line(v_query||' inserting others');
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line(' Error while inserting data in emp_tab for other then clerk --> '||SQLCODE||'---'||SQLERRM);
END;
END IF;
END LOOP;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001,'Error occurred in the plsql block',TRUE);
END;
/

Oracle PL/SQL capture lock exception in loop and continue

I have a procedure below, which loops through a cursor does some logic. I have put FOR UPDATE NOWAIT on the cursor to lock my records set, in case someone from another session wants to update the same set.
But there may be a scenario where the records I have selected in my cursor, is locked by someone as well, in which case, I want to simply log the locked record in a log table and continue to the next record in the loop.
PROCEDURE test(p_id IN NUMBER)
IS
CURSOR cur_test IS
SELECT emp_id,
emp_name
FROM
EMP
FOR UPDATE NOWAIT;
row_locked EXCEPTION;
PRAGMA EXCEPTION_INIT(row_locked, -54);
BEGIN
FOR r_cur_test IN cur_test
LOOP
BEGIN
--do something
EXCEPTION
WHEN row_locked THEN
--log locked record in log table
log_lock('Record is locked');
COMMIT;
END;
END LOOP;
--call log function to log a successful run
COMMIT;
EXCEPTION
WHEN OTHERS THEN
--Unsuccessful completion of the run.Trap all unhandled exceptions.
--log error in error table
log_lock('Process exited with error');
ROLLBACK;
END;
for testing, I open session 1 and executed
select * from EMP FOR UPDATE NOWAIT
and in session 2, I executed
begin
test(1);
end;
it always went to the OTHERS exception, as a result of which, the procecure just terminated without continuing looping through the cursor.
Can anyone give me some advice? Thanks a lot
When you do open cursor for update this statement works in scenario: at first run select query and set lock for all records and after then do fetch operations.
PROCEDURE test(p_id IN NUMBER)
IS
CURSOR cur_test IS
SELECT emp_id,
emp_name,
rowid as row_id
FROM
EMP;
row_locked EXCEPTION;
PRAGMA EXCEPTION_INIT(row_locked, -54);
v_sql varchar2(4000) := 'SELECT 1 FROM EMP t WHERE rowid = :row_id FOR UPDATE NOWAIT';
c int;
n int;
BEGIN
c := dbms_sql.open_cursor;
dbms_sql.parse(c, v_sql, dbms_sql.native);
FOR r_cur_test IN cur_test
LOOP
BEGIN
dbms_sql.bind_variable (c, 'row_id', i.row_id);
n := dbms_sql.execute(c);
--do something
EXCEPTION
WHEN row_locked THEN
--log locked record in log table
log_lock('Record is locked');
COMMIT;
END;
END LOOP;
dbms_sql.close_cursor(c);
--call log function to log a successful run
COMMIT;
EXCEPTION
WHEN OTHERS THEN
--Unsuccessful completion of the run.Trap all unhandled exceptions.
--log error in error table
log_lock('Process exited with error');
dbms_sql.close_cursor(c);
ROLLBACK;
END;

Is there a way to get the parameter values bound to a statement that caused an error from an eventtrigger?

Is there anyway to get the input parameter values, if any, bound to the a statement that caused an error in the database from a trigger? Regardless if the statement in SQL och a procedure/function call.
I'm trying to set up a log of errors on a schema level, which works fine. But I can't find a way to log the parameter values as well, and that would really be helpfull.
So far the trigger I'm using looks like this...
CREATE OR REPLACE TRIGGER t_error
AFTER SERVERERROR
ON SCHEMA
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
l_sql ora_name_list_t;
l_sequence NUMBER;
l_statement CLOB;
l_count NUMBER;
l_ts TIMESTAMP (6);
BEGIN
l_count := ora_sql_txt (l_sql);
IF l_count >= 1 THEN
FOR i IN 1 .. l_count LOOP
l_statement := l_statement || l_sql (i);
END LOOP;
END IF;
INSERT INTO oralog
VALUES (
seq_oralog.NEXTVAL,
dbms_utility.format_call_stack,
dbms_utility.format_error_stack,
dbms_utility.format_error_backtrace,
l_statement,
SYSTIMESTAMP
);
COMMIT;
END t_error;
/
Thanks

Resources