Calling Cursor Value into Exception - oracle

I have a cursor as part of a package, which compares the counts of 2 tables. If the counts match, rest of the package executes. But if it fails, it should update the counts in a log table.
The cursor code is
CURSOR C_CNT IS
SELECT
lnd.sml_batchrun_id batch_id,
lnd.lnd_count,
dwh.dwh_count
FROM
(
SELECT
sml_batchrun_id,
COUNT(*) lnd_count
FROM
iva_landing.lnd_sml_t
GROUP BY
sml_batchrun_id
) lnd
LEFT JOIN (
SELECT
batchrun_id,
sent_records_count dwh_count,
dwh_sending_table
FROM
dwh.dwh_idh_to_iva_metadata_t
) dwh ON dwh.batchrun_id = lnd.sml_batchrun_id
WHERE dwh.dwh_sending_table = 'DWH_SML_T'
ORDER BY
1 DESC;
The comparison code is:
FOR L_COUNT IN C_CNT LOOP --0001,0002
IF L_COUNT.lnd_count = L_COUNT.dwh_count THEN
UPDATE DWH.DWH_IDH_TO_IVA_METADATA_T idh
SET idh.IVA_RECEIVING_TABLE = 'LND_SML_T',
idh.RECEIVED_DATE = SYSDATE,
idh.RECEIVED_RECORDS_COUNT = L_COUNT.lnd_count,
idh.status = 'Verified'
WHERE L_COUNT.batch_id = idh.batchrun_id
AND idh.dwh_sending_table = 'DWH_SML_T';
COMMIT;
ELSE
RAISE EXCPT_SML_MISSDATA; -- Throw error and exit process
END IF;
END LOOP;
Now, in the Exception Handling part, I want to display the counts in the error column of the log table
logger.log_error('IVA-MSG 200-010 - COUNT MISMATCH! - Aborting', p_log_id=>l_job_log.log_id);
l_job_log.end_date := systimestamp;
l_job_log.error_mesg := cur_cnt.dwh_count||' '|| cur_cnt.lnd_count;
l_job_log.status := iva_log.iva_job_log_pck.c_str_statusfailed;
iva_log.iva_job_log_pck.change_joblog_prc(pi_rec_joblog => l_job_log);
RAISE;
Here, cur_cnt is a variable defined as cur_cnt c_cnt%rowtype;
and l_job_log as l_job_log iva_log.iva_job_log_t%rowtype;
where iva_job_log_t is the log table name.
But after triggering the package the count is not visible in the error column. Also, if I put something in single quotes for the iva_job_log_t then it gets displayed in the log table.
Please suggest how to display the counts from the cursor.
Thanks

If you are still doing
FOR L_COUNT IN C_CNT LOOP
then your cur_cnt variable is never going to be populated; it will be the empty record it was declared as, so referring to any of its fields will always give you null.
You can just refer to the l_count values in the error log:
l_job_log.error_mesg := l_count.dwh_count ||' '|| l_count.lnd_count;
... as you do elsewhere inside the loop.
If the exception handler is currently later in the process, so l_count is out of scope (as the re-raise possibly suggests) then you could move it so it's within the else instead of being separated:
FOR L_COUNT IN C_CNT LOOP --0001,0002
IF L_COUNT.lnd_count = L_COUNT.dwh_count THEN
UPDATE DWH.DWH_IDH_TO_IVA_METADATA_T idh
SET idh.IVA_RECEIVING_TABLE = 'LND_SML_T',
idh.RECEIVED_DATE = SYSDATE,
idh.RECEIVED_RECORDS_COUNT = L_COUNT.lnd_count,
idh.status = 'Verified'
WHERE L_COUNT.batch_id = idh.batchrun_id
AND idh.dwh_sending_table = 'DWH_SML_T';
COMMIT;
ELSE
logger.log_error('IVA-MSG 200-010 - COUNT MISMATCH! - Aborting', p_log_id=>l_job_log.log_id);
l_job_log.end_date := systimestamp;
l_job_log.error_mesg := l_count.dwh_count||' '|| l_count.lnd_count;
l_job_log.status := iva_log.iva_job_log_pck.c_str_statusfailed;
iva_log.iva_job_log_pck.change_joblog_prc(pi_rec_joblog => l_job_log);
RAISE EXCPT_SML_MISSDATA; -- Throw error and exit process
END IF;
END LOOP;
Otherwise you would have to change the cursor loop handling to fetch into the variable, refer to that within the loop instead, and rely on that having the right values later:
OPEN C_CNT
LOOP
FETCH C_CNT INTO CUR_CNT;
EXIT WHEN C_CNT%NOTFOUND;
IF CUR_CNT.lnd_count = CUR_CNT.dwh_count THEN
UPDATE DWH.DWH_IDH_TO_IVA_METADATA_T idh
SET idh.IVA_RECEIVING_TABLE = 'LND_SML_T',
idh.RECEIVED_DATE = SYSDATE,
idh.RECEIVED_RECORDS_COUNT = CUR_CNT.lnd_count,
idh.status = 'Verified'
WHERE CUR_CNT.batch_id = idh.batchrun_id
AND idh.dwh_sending_table = 'DWH_SML_T';
COMMIT;
ELSE
RAISE EXCPT_SML_MISSDATA; -- Throw error and exit process
END IF;
END LOOP;
Your later exception handler should then work as it is.

Related

NO DATA FOUND oracle using cursor

I have been searching online using different solution suggestion to handle no data in this code but to no avail. how can I handle the exception if no data is found. How can I solve this problem. I am not an expert in oracle though!
DECLARE
nCheckOption INT;
no_data_found EXCEPTION;
CURSOR TYPE_cursor IS
SELECT
D_NAL_REF.TRANS
, D_NAL_REF.INJ
, D_NAL_REF.REF
FROM D_NAL_REF D_NAL_REF
WHERE D_NAL_REF.REF IN
(SELECT AG_REF.REF
FROM AG_REF A_REF
WHERE A_REF.DESCEND_REF = 10
);
BEGIN
FOR rec IN TYPE_cursor
LOOP
nCheckOption := 0;
SELECT 1
INTO nCheckOption
FROM PERSON_TYPE WHERE TRANS = rec.TRANS AND INJ = rec.INJ;
IF nCheckOption = 1 THEN
UPDATE PERSON_TYPE
SET PERSON_TYPE.TYPE = rec.REF
WHERE TRANS = rec.TRANS
AND PERSON_TYPE.INJ = rec.INJ;
END IF;
EXCEPTION
WHEN no_data_found
THEN
DBMS_OUTPUT.PUT_LINE ('Trapped the error!?');
END LOOP;
END;
/
Rewrite your code to eliminate the inner SELECT, which is the only place in your code where I can see that a NO_DATA_FOUND exception could possibly be raised:
BEGIN
FOR rec IN (SELECT d.TRANS,
d.INJ,
d.REF
FROM D_NAL_REF d
WHERE d.REF IN (SELECT a.REF
FROM AG_REF a
WHERE a.DESCEND_REF = 10) AND
(d.TRANS, d.INJ) IN (SELECT DISTINCT TRANS, INJ
FROM PERSON_TYPE))
LOOP
UPDATE PERSON_TYPE
SET TYPE = rec.REF
WHERE TRANS = rec.TRANS AND
INJ = rec.INJ;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE ('Trapped the error!?');
END;
I think you need to find if cursor contains any record or not. If cursor is empty then it must return that error message written in exception block.
Or you want to print error message if update statement do not find any record to update.
Here is the pseudo code.
Declare
ncheckoption number := 0;
Cursor type_cursor is ....
Begin
For rec in type_cursor loop
ncheckoption := ncheckoption + 1;
Update ...
If sql%rowcount = 0 then
Dbms_output.put_line('error message, if you want here in case no record found in table to update');
-- loop will continue
-- if you want loop to break then issue exit statement here
End if;
End loop;
If ncheckoption = 0 then
Dbms_output.put_line('error message you want to print in case cursor is empty');
End if;
End;
/
Cheers!!

Check record exist before update

I want to update a table but I want to check if the record exist. If not then throw an exception. In the C# application I pass the parameters and execute the command by the following.
procedure usp_update_example
(
p_id in mydb.member.idn_member%type,
p_idn_person in mydb.member.idn_person%type,
p_ind_rep in mydb.member.ind_rep%type
)
as
v_exist pls_integer := 0;
v_step varchar2(250);
v_exception_not_exist exception;
begin
v_step := 'Check for record ' || p_id;
select count(1)
into v_exist
from mydb.member
where idn_member = p_id;
if v_exist = 0 then
raise v_exception_not_exist;
end if;
if (v_exist > 0) then
v_step := 'Update table :' || p_id;
update mydb.member
set
idn_person = p_idn_person,
ind_rep = p_ind_rep
where idn_member = p_id;
end if;
exception
when v_exception_not_exist then
Raise_application_error(-20001, 'Not exist');
end usp_update_example;
However even my condition is right, I do have the record existing in the table. I always get Not exist exception. If I don't use if v_exist = 0 and use WHEN NO_DATA_FOUND THEN. Then everything is fine.
I am not sure where is wrong.
Your code seems to be fine. Looks like this issue is related to some uncommitted data - you see the record in the session where you inserted it and you don't see it in C# session since the record is not committed yet. Hence, C# session generates exception.
What I would suggest re the procedure code is to make it more compact.
Something like the following:
...
begin
update mydb.member
set idn_person = p_idn_person,
ind_rep = p_ind_rep
where idn_member = p_id;
if SQL%ROWCOUNT = 0 then
raise_application_error(-20001,'Not exist');
end if;
end;

ORA-01007 "variable not in select list" from dbms_sql.column_value call

I am trying to use dynamic SQL to sample all the data in a schema with a pattern:
DECLARE
xsql varchar2(5000);
c NUMBER;
d NUMBER;
col_cnt INTEGER;
f BOOLEAN;
rec_tab DBMS_SQL.DESC_TAB;
col_num NUMBER;
varvar varchar2(500);
PROCEDURE print_rec(rec in DBMS_SQL.DESC_REC) IS
BEGIN
DBMS_OUTPUT.ENABLE(1000000);
DBMS_OUTPUT.NEW_LINE;
DBMS_OUTPUT.PUT_LINE('col_type = '
|| rec.col_type);
DBMS_OUTPUT.PUT_LINE('col_maxlen = '
|| rec.col_max_len);
DBMS_OUTPUT.PUT_LINE('col_name = '
|| rec.col_name);
DBMS_OUTPUT.PUT_LINE('col_name_len = '
|| rec.col_name_len);
DBMS_OUTPUT.PUT_LINE('col_schema_name = '
|| rec.col_schema_name);
DBMS_OUTPUT.PUT_LINE('col_schema_name_len = '
|| rec.col_schema_name_len);
DBMS_OUTPUT.PUT_LINE('col_precision = '
|| rec.col_precision);
DBMS_OUTPUT.PUT_LINE('col_scale = '
|| rec.col_scale);
DBMS_OUTPUT.PUT('col_null_ok = ');
IF (rec.col_null_ok) THEN
DBMS_OUTPUT.PUT_LINE('true');
ELSE
DBMS_OUTPUT.PUT_LINE('false');
END IF;
END;
BEGIN
c := DBMS_SQL.OPEN_CURSOR;
xsql:='
WITH got_r_num AS
(
SELECT e.* -- or whatever columns you want
, ROW_NUMBER () OVER (ORDER BY dbms_random.value) AS r_num
FROM dba_tab_columns e
)
SELECT * -- or list all columns except r_num
FROM got_r_num
WHERE r_num <= 10';
DBMS_SQL.PARSE(c, xsql, DBMS_SQL.NATIVE);
d := DBMS_SQL.EXECUTE(c);
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
LOOP
IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
NULL;
-- get column values of the row
DBMS_SQL.COLUMN_VALUE(c, 2, varvar);
--dbms_output.put_line('varvar=');
--DBMS_SQL.COLUMN_VALUE(source_cursor, 2, name_var);
--DBMS_SQL.COLUMN_VALUE(source_cursor, 3, birthdate_var);
-- Bind the row into the cursor that inserts into the destination table. You
-- could alter this example to require the use of dynamic SQL by inserting an
-- if condition before the bind.
--DBMS_SQL.BIND_VARIABLE(destination_cursor, ':id_bind', id_var);
--DBMS_SQL.BIND_VARIABLE(destination_cursor, ':name_bind', name_var);
--DBMS_SQL.BIND_VARIABLE(destination_cursor, ':birthdate_bind',
--birthdate_var);
--ignore := DBMS_SQL.EXECUTE(destination_cursor);
--ELSE
-- No more rows to copy:
--EXIT;
END IF;
END LOOP;
--EXIT WHEN d != 10;
--END LOOP;
col_num := rec_tab.first;
IF (col_num IS NOT NULL) THEN
LOOP
print_rec(rec_tab(col_num));
col_num := rec_tab.next(col_num);
EXIT WHEN (col_num IS NULL);
END LOOP;
END IF;
DBMS_SQL.CLOSE_CURSOR(c);
END;
/
When I run that it gives me this error from the line with the dbms_sql.column_value call:
ORA-01007: variable not in select list
If I comment out that dbms_sql.column_value call it still errors but now with:
ORA-01002: fetch out of sequence
What am I doing wrong?
You have two problems in the code you posted. Firstly you have skipped part of the execution flow because you haven't called the DEFINE_COLUMN procedure. That is what is causing the ORA-01007 error, as the dynamic SQL processing hasn't been told about the select list columns via that call. For your current code you only need to define column 2, but assuming you will actually want to refer to the others you can define them in a loop. To treat them all as string for display you could do:
...
DBMS_SQL.PARSE(c, xsql, DBMS_SQL.NATIVE);
d := DBMS_SQL.EXECUTE(c);
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
FOR i IN 1..col_cnt
LOOP
-- dbms_output.put_line('col_name is ' || rec_tab(i).col_name);
DBMS_SQL.DEFINE_COLUMN(c, i, varvar, 500);
END LOOP;
LOOP
IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
...
If you want to do anything that needs to treat the variables as the right types you could have a local variable of each type and use the data type from the rec_tab information you already have from describe_columns to use the appropriately typed variable for each column.
The second problem, which you were hitting when you commented the column_value call, is still there once that definbe issue has been fixed. Your loop doesn't ever exit, so after you fetch the last row from the cursor you do a further invalid fetch, which throws ORA-01002. You have the code to avoid that already but it's commented out:
...
LOOP
IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
-- get column values of the row
DBMS_SQL.COLUMN_VALUE(c, 2, varvar);
...
ELSE
-- No more rows to copy:
EXIT;
END IF;
END LOOP;
...
With those two changes your code runs, and dumps the view structure:
PL/SQL procedure successfully completed.
col_type = 1
col_maxlen = 30
col_name = OWNER
col_name_len = 5
col_schema_name =
col_schema_name_len = 0
col_precision = 0
col_scale = 0
col_null_ok = false
col_type = 1
col_maxlen = 30
col_name = TABLE_NAME
...
To those who find this question when accessing Oracle through ODP.NET, as I did:
We started getting this error whenever we would add column to an existing table in our application. I'm not sure what all the conditions were to make it fail, but ours were:
Run a SELECT * FROM "table".
Include a ROWNUM restriction in the WHERE clause (WHERE ROWNUM < 10).
Run that through the ODP.NET dataReader.GetSchemaTable() call.
Running unrestricted queries or running queries directly on Oracle SQL Developer did not seem to cause the error.
I've hit some pretty weird stuff in the past with Oracle connection pooling, so I eventually thought that could be the problem. The solution was to restart the web service to force all the connections to be fully dropped and recreated.
The theory is that the ODP.NET connection from the connection pool still had no idea the column existed on the table, but the column was returned by the database.

How do you make DBMS_DATAPUMP error if there's an error?

DBMS_DATAPUMP doesn't fail when the columns in the source and destination tables do not match. This means that no exceptions are raised. I'm trying to use the GET_STATUS procedure in order to understand if there are any errors but unfortunately there doesn't seem to be...
My ultimate goal is for DBMS_DATAPUMP to raise an exception if the import fails. Differing columns is an easy example to work with as I know that it should fail.
Here's my current code (I've obscured schema names purposefully). The environment I'm using is identical on both servers save that I've added an extra column to the source table. I also perform a count of the number of rows in the table.
connect schema/*#db1/db1
-- */
create table tmp_test_datapump as
select u.*, cast(null as number) as break_it
from user_tables u;
Table created.
select count(*) from tmp_test_datapump;
COUNT(*)
----------
1170
connect schema/*#db2/db2
-- */
set serveroutput on
create table tmp_test_datapump as
select u.*
from user_tables u;
Table created.
In attempting to test this the DATAPUMP code has got a little more complicated. Everything in the infinite loop can be removed and this would act the same.
declare
l_handle number;
l_status varchar2(255);
l_job_state varchar2(4000);
l_ku$status ku$_status1020;
begin
l_handle := dbms_datapump.open( operation => 'IMPORT'
, job_mode => 'TABLE'
, remote_link => 'SCHEMA.DB.DOMAIN.COM'
, job_name => 'JOB_TEST_DP'
, version => 'COMPATIBLE' );
dbms_datapump.set_parameter( handle => l_handle
, name => 'TABLE_EXISTS_ACTION'
, value => 'TRUNCATE');
dbms_datapump.metadata_filter( handle => l_handle
, name => 'NAME_EXPR'
, value => 'IN (''TMP_TEST_DATAPUMP'')');
dbms_datapump.start_job(handle => l_handle);
while true loop
dbms_datapump.wait_for_job(handle => l_handle,job_state => l_status);
if l_status in ('COMPLETED','STOPPED') then
exit;
end if;
dbms_datapump.get_status( handle => l_handle
, mask => dbms_datapump.KU$_STATUS_JOB_ERROR
, job_state => l_job_state
, status => l_ku$status);
dbms_output.put_line('state: ' || l_job_state);
if l_ku$status.error is not null and l_ku$status.error.count > 0 then
for i in l_ku$status.error.first .. l_ku$status.error.last loop
dbms_output.put_line(l_ku$status.error(i).logtext);
end loop;
end if;
end loop;
end;
/
PL/SQL procedure successfully completed.
select count(*) from tmp_test_datapump;
COUNT(*)
----------
47
As you can see the number of records in the tables is different; the import has failed and no exception has been raised. Various blogs and DBA.SE questions imply that some sort of error catching can be done; but I can't seem to manage it.
How can you catch fatal errors in a DBMS_DATAPUMP import?
I'm working with dbms_datapump package right know. The following procedure is searching one table for schemas that will be exported. BACKUP_INFO_MOD is a procedure with PRAGMA AUTONOMOUS TRANSACTION that's making logs in another table.
Example 6.3 from this document helped me a lot. Here's fragment from my code (with additional commentary):
CREATE OR REPLACE PROCEDURE BACKUP_EXECUTE (
threads in number := 1
, dir in varchar2 := 'DATA_PUMP_DIR'
) AS
schemas varchar2(255);
filename varchar2(255);
path varchar2(255);
errormsg varchar2(4000);
handle number;
job_state varchar2(30);
--variables under this line are important to error handling
logs ku$_LogEntry;
lindx pls_integer;
status ku$_Status;
exporterr exception; --our exception to handle export errors
[...]
BEGIN
[...]
schemas:=schema_list(indx).schema_name;
--Full dir path for logs
select directory_path into path from dba_directories where directory_name=dir;
--If data not found then automatically raise NO_DATA_FOUND
select to_char(sysdate, 'YYMMDDHH24MI-')||lower(schemas)||'.dmp' into filename from dual;
backup_info_mod('insert',path||filename,schemas);
begin --For inner exception handling on short fragment
handle := dbms_datapump.open('EXPORT','SCHEMA');
dbms_datapump.add_file(handle, filename, dir); --dump file
dbms_datapump.add_file(handle, filename||'.log', dir,null,DBMS_DATAPUMP.KU$_FILE_TYPE_LOG_FILE); --export log file
dbms_datapump.metadata_filter(handle, 'SCHEMA_EXPR', 'IN ('''||schemas||''')');
dbms_datapump.set_parallel(handle,threads);
backup_info_mod(file_name=>path||filename, curr_status=>'IN PROGRESS');
dbms_datapump.start_job(handle);
--If job didn't start due to some errors, then let's get some information
exception
when others then
dbms_datapump.get_status(handle,8,0,job_state,status);
--This will overwrite our job_state and status
end;
--Let's go handle error if job_state was overwritten
if job_state is not null then
raise exporterr;
else
job_state:='UNDEFINED';
end if;
--Checking in loop if errors occurred. I'm not using wait_for_job
--because it didn't work out
while (job_state != 'COMPLETED') and (job_state != 'STOPPED') loop
--Like before, let's get some information
dbms_datapump.get_status(handle,8,-1,job_state,status);
--Looking for errors using mask
if (bitand(status.mask,dbms_datapump.ku$_status_job_error) != 0) then
--If occurred: let's stop the export job and raise an error
dbms_datapump.stop_job(handle);
dbms_datapump.detach(handle);
raise exporterr;
exit;
end if;
end loop;
backup_info_mod(file_name=>path||filename, curr_status=>'COMPLETED');
dbms_datapump.detach(handle);
exception
when NO_DATA_FOUND then
backup_info_mod('insert',null,schemas,'ERROR','No '||dir||' defined in dba_directories');
when exporterr then
--Let's get all error messages and write it to errormsg variable
logs:=status.error;
lindx:=logs.FIRST;
while lindx is not null loop
errormsg:=errormsg||logs(lindx).LogText;
lindx:=logs.NEXT(lindx);
if lindx is not null then
errormsg:=errormsg||' | '; --Just to separate error messages
end if;
end loop;
backup_info_mod(
file_name=>path||filename,
curr_status=>'ERROR',
errormsg=>errormsg);
/*when other then --TODO
null;
*/
end;
END BACKUP_EXECUTE;
You can put the datapump command in a shell script when the log is created for the impdp, and before you end the shell script you can check the log for IMP- errors or ORA- errors, if true warn the user to look at the log file for errors.
The provided document from yammy is good.
I faced the same problem when using the dbms_datapump package to import a DB dump. Even there are error during the import, the job is treated as success / finished. By using the code example in the document, i could get the error / log message during the import. I then check if there are 'ORA-' found in the log message and throw a custom error when the import job is finished.
Following is the sample code:
PROMPT CREATE OR REPLACE PROCEDURE import_schema
CREATE OR REPLACE PROCEDURE import_db_dump (
dumpFilename IN VARCHAR2)
IS
handle NUMBER; -- Handler of the job
loopIdx NUMBER; -- Loop index
percentDone NUMBER; -- Percentage of job complete
jobState VARCHAR2(30); -- To keep track of job state
ku_logEntry ku$_LogEntry; -- For WIP and error messages
ku_jobStatus ku$_JobStatus; -- The job status from get_status
ku_jobDescjd ku$_JobDesc; -- The job description from get_status
ku_Status ku$_Status; -- The status object returned by get_status
errorCount NUMBER;
import_error_found EXCEPTION;
BEGIN
handle := dbms_datapump.open (
operation => 'IMPORT',
job_mode => 'SCHEMA');
dbms_output.put_line('Define table exists action: truncate');
dbms_datapump.set_parameter (
handle => handle,
name => 'TABLE_EXISTS_ACTION',
value => 'TRUNCATE');
dbms_output.put_line('Define dumpfilename: ' || dumpFilename);
dbms_datapump.add_file (
handle => handle,
filename => dumpFilename,
filetype => dbms_datapump.ku$_file_type_dump_file);
dbms_output.put_line('Start datapump job');
dbms_output.put_line('==================');
dbms_datapump.start_job (handle);
-- Ref: http://docs.oracle.com/cd/E11882_01/server.112/e22490/dp_api.htm#SUTIL977
-- The import job should now be running. In the following loop, the job is
-- monitored until it completes. In the meantime, progress information is
-- displayed.
percentDone := 0;
jobState := 'UNDEFINED';
errorCount := 0;
WHILE (jobState != 'COMPLETED') AND (jobState != 'STOPPED') LOOP
dbms_datapump.get_status(handle,
dbms_datapump.ku$_status_job_error +
dbms_datapump.ku$_status_job_status +
dbms_datapump.ku$_status_wip,
-1, jobState, ku_Status);
ku_jobStatus := ku_Status.job_status;
-- If the percentage done changed, display the new value.
IF ku_jobStatus.percent_done != percentDone THEN
dbms_output.put_line('*** Job percent done = ' ||
to_char(ku_jobStatus.percent_done));
percentDone := ku_jobStatus.percent_done;
END IF;
-- If any work-in-progress (WIP) or Error messages were received for the job,
-- display them.
IF (bitand(ku_Status.mask, dbms_datapump.ku$_status_wip) != 0) THEN
ku_logEntry := ku_Status.wip;
ELSE
IF (bitand(ku_Status.mask,dbms_datapump.ku$_status_job_error) != 0) THEN
ku_logEntry := ku_Status.error;
ELSE
ku_logEntry := null;
END IF;
END IF;
IF ku_logEntry IS NOT NULL THEN
loopIdx := ku_logEntry.FIRST;
WHILE loopIdx IS NOT NULL LOOP
dbms_output.put_line(ku_logEntry(loopIdx).LogText);
IF INSTR(ku_logEntry(loopIdx).LogText, 'ORA-') > 0 THEN
errorCount := errorCount + 1;
dbms_output.put_line('^^^^---ERROR FOUND');
END IF;
loopIdx := ku_logEntry.NEXT(loopIdx);
END LOOP;
END IF;
END LOOP;
-- Indicate that the job finished and gracefully detach from it.
dbms_output.put_line('Job has completed');
dbms_output.put_line('Final job state = ' || jobState);
dbms_datapump.detach(handle);
IF errorCount > 0 THEN
RAISE import_error_found;
END IF;
EXCEPTION
WHEN import_error_found THEN
dbms_output.put_line('Error found when import. Number of error: ' || errorCount);
RAISE;
WHEN OTHERS THEN
dbms_output.put_line('[Error Backtrace]');
dbms_output.put_line(dbms_utility.format_error_backtrace());
dbms_output.put_line('[Call Stack]');
dbms_output.put_line(dbms_utility.format_call_stack());
dbms_datapump.stop_job(handle);
RAISE;
END;
/
Hope this help.

Is it possible to CONTINUE a loop from an exception?

I have a fetch being executed inside of a loop. If this fetch fails (no data) I would like to CONTINUE the loop to the next record from within the EXCEPTION.
Is this possible?
I'm getting a ORA-06550 & PLS-00201 identifer CONTINUE must be declared
DECLARE
v_attr char(88);
CURSOR SELECT_USERS IS
SELECT id FROM USER_TABLE
WHERE USERTYPE = 'X';
BEGIN
FOR user_rec IN SELECT_USERS LOOP
BEGIN
SELECT attr INTO v_attr
FROM ATTRIBUTE_TABLE
WHERE user_id = user_rec.id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- user does not have attribute, continue loop to next record.
CONTINUE;
END;
END LOOP;
END;
The CONTINUE statement is a new feature in 11g.
Here is a related question: 'CONTINUE' keyword in Oracle 10g PL/SQL
In the construct you have provided, you don't need a CONTINUE. Once the exception is handled, the statement after the END is performed, assuming your EXCEPTION block doesn't terminate the procedure. In other words, it will continue on to the next iteration of the user_rec loop.
You also need to SELECT INTO a variable inside your BEGIN block:
SELECT attr INTO v_attr FROM attribute_table...
Obviously you must declare v_attr as well...
How about the ole goto statement (i know, i know, but it works just fine here ;)
DECLARE
v_attr char(88);
CURSOR SELECT_USERS IS
SELECT id FROM USER_TABLE
WHERE USERTYPE = 'X';
BEGIN
FOR user_rec IN SELECT_USERS LOOP
BEGIN
SELECT attr INTO v_attr
FROM ATTRIBUTE_TABLE
WHERE user_id = user_rec.id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- user does not have attribute, continue loop to next record.
goto end_loop;
END;
<<end_loop>>
null;
END LOOP;
END;
Just put end_loop at very end of loop of course. The null can be substituted with a commit maybe or a counter increment maybe, up to you.
For this example you really should just use an outer join.
declare
begin
FOR attr_rec IN (
select attr
from USER_TABLE u
left outer join attribute_table a
on ( u.USERTYPE = 'X' and a.user_id = u.id )
) LOOP
<process records>
<if primary key of attribute_table is null
then the attribute does not exist for this user.>
END LOOP;
END;
Notice you can use WHEN exception THEN NULL the same way as you would use WHEN exception THEN continue. Example:
DECLARE
extension_already_exists EXCEPTION;
PRAGMA EXCEPTION_INIT(extension_already_exists, -20007);
l_hidden_col_name varchar2(32);
BEGIN
FOR t IN ( SELECT table_name, cast(extension as varchar2(200)) ext
FROM all_stat_extensions
WHERE owner='{{ prev_schema }}'
and droppable='YES'
ORDER BY 1
)
LOOP
BEGIN
l_hidden_col_name := dbms_stats.create_extended_stats('{{ schema }}', t.table_name, t.ext);
EXCEPTION
WHEN extension_already_exists THEN NULL; -- ignore exception and go to next loop iteration
END;
END LOOP;
END;

Resources