So I made a bug in which I didn't put execute immediate inside nested begin-end block so my code didn't work. So basically I had
begin
execute immediate 'select * from sales';
exception when others then null;
dbms_output.put_line(123);
end;
Dbms_output did not print "123" and I figured out I need to put execute immediate inside begin-end. I want to be sure, so my question is why is code not executing after exception part even though exception is not raised?
why is code not executing after exception part even though exception is not raised?
The reason is very simple - dbms_output.put_line in the question is not in "after exception part".
Properly indented code is equivalent to:
begin
execute immediate 'select * from dual';
exception when others then
null;
dbms_output.put_line(123);
end;
/
Exception block is everything between EXCEPTIONS and END; and will be executed if an exception occurs.
A couple of things in play here - parsing and execution.
If the table does not exist, then the statement cannot be parsed, and hence we'll get an exception immediately.
SQL> begin
2 execute immediate 'select * from xxxx';
3 end;
4 /
begin
*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at line 2
However, if the table DOES exist then the parse will be fine. Because you never specified an INTO that is all we do. We never needed to execute and commence the fetch phase and thus no error occurred. An easy to way to see that we never executed the statement is with something like
SQL> begin
2 execute immediate 'select 1/count(*) from dual where 1=2';
3 end;
4 /
PL/SQL procedure successfully completed.
If we had executed, then we'd expect a divide by zero error to come out. The moment we see an INTO, then we'll need to fetch (which will require an execution).
Related
I would like to create the following index
CREATE INDEX timevariable_idx_varvalue_projectid
ON timevariable (varvalue,projectid);
only if does not exist, but I'm having a hard time doing it
would anyone know how to do so?!
Unfortunately Oracle does not support the IF NOT EXISTS clause to CREATE statements (I don't know about APEX though, which you also tagged your question with).
So you need execute immediate in a code block. This Ask TOM article gives an elegant solution that works by trapping exceptions.
Adapted to your use case, this would be:
set serveroutput on
declare
already_exists exception;
columns_indexed exception;
pragma exception_init(already_exists, -955);
pragma exception_init(columns_indexed, -1408);
begin
execute immediate 'create index timevariable_idx_varvalue_projectid ON timevariable (varvalue,projectid)';
dbms_output.put_line('created');
exception
when already_exists or columns_indexed then
dbms_output.put_line('skipped');
end;
Oracle has no "IF NOT EXISTS" syntax in its DDL commands. If you execute the CREATE command, you either need to accept the error in your script as ok to ignore, or handle the error in some way. If you want to avoid the execute commend unless necessary, you'd need to check the data dictionary for the index first, then execute if required:
declare
l_count number;
begin
select count(*) into l_count from dba_indexes where index_name='TIMEVARIABLE_IDX_VARVALUE_PROJECTID';
if l_count = 0 then
execute immediate 'CREATE INDEX timevariable_idx_varvalue_projectid ON timevariable (varvalue,projectid)';
end if;
end;
I've wrapper procedure which looks like this:
Procedure A
BEGIN;
Procedure B
Procedure C
END;
/
Procedure B drops and re-creates a table used in Procedure C. Due to this, I get the below error:
ERROR at line 1:
ORA-04068: existing state of packages has been discarded ORA-04065: not executed, altered or dropped stored procedure "SCHEMA.PROCEDURE C"
ORA-06508: PL/SQL: could not find program unit being called: "SCHEMA.PROCEDURE C"
ORA-06512: at "SCHEMA.PROCEDURE A", line 4
ORA-06512: at line 1
How do I go about getting this procedure to work?
So your proc get invalid during the execution, that is why you need recompile Procedure C. To achive this, you can call a dynamic SQL statement:
EXECUTE IMMEDIATE 'ALTER PROCEDURE my_procedure COMPILE';
Procedure_A IS
BEGIN;
Procedure_B;
EXECUTE IMMEDIATE 'ALTER PROCEDURE Procedure_C COMPILE';
Procedure_C;
END;
/
I recommend using dynamic SQL to call the DML in procedure C. By removing the dependency nothing gets invalidated. This would likely be more reliable than dynamically re-compiling code or dynamically running the code and catching all exceptions.
You can see this in the below sample code. Comment out the static SQL call on line 10 in procedure C to replicate the error.
drop table drop_me;
create table drop_me(id number);
create or replace procedure C is
v_count number;
begin
--Static SQL would fail with this error:
-- ORA-04068: existing state of packages has been discarded
-- ORA-04065: not executed, altered or dropped stored procedure "JHELLER.C"
-- ORA-06508: PL/SQL: could not find program unit being called: "JHELLER.C"
-- ORA-06512: at "JHELLER.A", line 4
-- ORA-06512: at line 2
--select count(*) into v_count from drop_me;
--Dynamic SQL runs correctly:
execute immediate 'select count(*) from drop_me' into v_count;
end;
/
create or replace procedure B is
begin
execute immediate 'drop table drop_me purge';
execute immediate 'create table drop_me(id number)';
end;
/
create or replace procedure A is
begin
B;
C;
end;
/
begin
A;
end;
/
But I agree with Alex Poole that there is almost certainly a better way to do this without dropping and re-creating objects. I can think of a few reasons why a DROP and CREATE might run faster than a TRUNCATE and INSERT, but only because of some weird side-effect, like dropping bad table statistics. Investigate the difference more and you might find the real reason.
You can do something like it.
begin
procedureB;
begin
execute immediate 'begin procedureC; end;';
exception when others then
execute immediate 'begin procedureC; end;';
end;
end;
I am writing a deployment script that includes a MERGE into a view that may or may not exist in one of many target schemas (schemii? schemata?).
I am attempting this SQL:
DECLARE
BEGIN
/* MERGE statement that will work if the view is available */
EXCEPTION
WHEN OTHERS THAN
DBMS_OUTPUT.put_line('warning: merge target is not available');
END;
When I run this block I get the standard "PL/SQL: ORA-00942: table or view does not exists" error.
How can I trap this error and generate a warning line instead?
Combining the valid answers with the suggested improvements, it all came together as:
SET SERVEROUTPUT ON;
PROMPT ...trying UPSERT;
DECLARE
eTableNotExists exception;
pragma exception_init(eTableNotExists, -00942);
BEGIN
EXECUTE IMMEDIATE '<MERGE statement that will work if the view is available--no trailing ";"!>';
DBMS_OUTPUT.put_line('insert worked');
EXCEPTION
WHEN eTableNotExists THEN
DBMS_OUTPUT.put_line('FYI: doesn''t exist on this schema');
END;
/
COMMIT;
SET SERVEROUTPUT OFF;
(SERVEROUTPUT sets were necessary to see results of DBMS_OUTPUT.put_line())
You need to use dynamic SQL:
BEGIN
EXECUTE IMMEDIATE 'MERGE ...';
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('warning: merge target is not available');
END;
You can use dynamic SQL to postpone such an error to run time and catch it
EXECUTE IMMEDIATE '<MERGE>'
instead of "static" MERGE
I am raising an exception in a subprogram, and I'm expecting to see the calling function halt execution at this point. However, the calling function continues processing as if nothing happened, and I don't understand why.
My function looks something like this:
FUNCTION getFooCursor (i_blah IN VARCHAR)
RETURN t_ref_cursor
IS
v_sum_variable NUMBER;
BEGIN
--lookup number
v_sum_variable := getNumber (i_blah);
--call function that raises NO_DATA_FOUND exception
doRaiseException();
--the exception handler is only supposed to catch for this block
BEGIN
--do stuff and end up with a cursor
RETURN barCursor(v_sum_variable);
EXCEPTION
WHEN OTHERS THEN
--set some variables
END
END;
Let's say doRaiseException() looks like this:
PROCEDURE doRaiseException ()
IS
BEGIN
RAISE NO_DATA_FOUND;
END;
When I debug this function in TOAD, it helpfully informs me that the NO_DATA_FOUND exception has been raised. However, it then immediately carries on to execute the next line (where barCursor() is called) and the function finishes as if nothing ever went wrong.
I have tried replacing doRaiseException(); directly with RAISE NO_DATA_FOUND; for testing purposes (it actually does more than that) and this stops execution within getFooCursor() but whatever SQL calls it again completely ignores the exception.
Is this just how exceptions work in PL/SQL? Don't they bubble up as they do in Java or C#? Perhaps I am missing something crucial about exceptions in Oracle. How do I get an exception to bubble up to the host?
Here is my Oracle version (as returned from v$version):
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bi
PL/SQL Release 10.2.0.5.0 - Production
CORE 10.2.0.5.0 Production
TNS for HPUX: Version 10.2.0.5.0 - Production
NLSRTL Version 10.2.0.5.0 - Production
Your understanding of exceptions is correct. However, this is one notable exception to how exceptions work: NO_DATA_FOUND is silently ignored in a SQL context. This is a "feature", because this is how Oracle tells other processes that there is no more data to read.
For custom exceptions, you will probably need to catch NO_DATA_FOUND and raise it as different exception. This is usually a horrible way to handle exceptions but there is no good alternative here.
SQL> create or replace function function1 return number is
2 begin
3 raise no_data_found;
4 return 1;
5 end;
6 /
Function created.
SQL> select function1 from dual;
FUNCTION1
----------
1 row selected.
SQL> create or replace function function2 return number is
2 begin
3 raise no_data_found;
4 return 1;
5 exception when no_data_found then
6 raise_application_error(-20000, 'NO_DATA_FOUND raised');
7 end;
8 /
Function created.
SQL> select function2 from dual;
select function2 from dual
*
ERROR at line 1:
ORA-20000: NO_DATA_FOUND raised
ORA-06512: at "JHELLER.FUNCTION2", line 6
Exceptions work as you suppose and "bubble up", so you must be catching it somewhere.
That's what's happening... you're catching every exception, which isn't the best practice. You can either ensure you only catch a specific exception if you define one yourself. However, that doesn't seem to be what you want to do here. You want to re-raise only a single exception.
So, you could define a custom exception in a separate package, raise that in your sub-program and then do something like this in your calling block:
begin
RaiseException;
exception
when my_exception_package.my_exception then
raise;
when others then
DoSomethingElse;
end;
That way you catch the exceptions you want to raise and then re-raise them. If the exception is different then you continue with your current programme flow.
Right now I'm importing and transforming data into an Oracle database as follows:
A program regularly polls specific folders, once a file is found it executes a batch file which does some light transformation in Python & bash and then calls SQL*Loader to load the CSV file into a staging table.
Then, the batch script calls an SQL script (via SQLPlus) to do the final transformation and insert the transformed data into master tables for their respective staging table.
The problem with this method is there's no error-handling on the SQLPlus side, eg. if an 'insert into' statement fails because of a violated constraint (or any other reason), it will still continue to execute the rest of the statements contained in the SQL script.
Ideally, if any exception occurs, I'd prefer all changes to be rolled back and details of the exception inserted into an etl log table.
Stored procedures seem to be a good fit as exception handling is built-in. However, I'm struggling with the syntax - specifically how I can take my big SQL scripts (which are just a combination of INSERT INTO, UPDATE, CREATE, DROP, DELETE, etc statements) and throw them into a stored procedure with some very basic error handling.
What I'm hoping for is either:
a quick & dirty dummy's guide to taking my depressing blob of PL/SQL and get it to execute within a stored procedure OR
Any alternative (if a stored proc isn't appropriate) which offers the same functionality, ie. a way to execute a bunch of SQL statements and rollback if any of these statements throw an exception.
About my attempts - I've tried copying portions of my SQL scripts into a stored procedure but they always fail to compile with the error 'PLS-00103 Encountered the symbol when expecting one of the following'. eg.
CREATE OR REPLACE PROCEDURE ETL_2618A AS
BEGIN
DROP SEQUENCE "METER_REPORTING"."SEQ_2618";
CREATE SEQUENCE SEQ_2618;
END ETL_2618A;
Oracle documentation isn't terribly accessible and I've not had much luck with googling/searching StackOverflow, but I apologise if I've missed something obvious.
To do DDL in PL/SQL you need to use dynamic sql.
CREATE OR REPLACE PROCEDURE testProc IS
s_sql VARCHAR2(500);
BEGIN
s_sql := 'DROP SEQUENCE "METER_REPORTING"."SEQ_2618"';
EXECUTE IMMEDIATE s_sql;
s_sql := 'CREATE SEQUENCE "METER_REPORTING"."SEQ_2618"';
EXECUTE IMMEDIATE s_sql;
EXCEPTION
WHEN OTHERS THEN
NULL;
end testProc;
/
If you run the script in sqlplus you can use:
whenever sqlerror
to control what should happen when an error occurs.
http://docs.oracle.com/cd/B19306_01/server.102/b14357/ch12052.htm
Adding exception handling to a PL/SQL proc or script isn't difficult, but of course some coding is required. Here's your procedure dressed up slightly with some very basic error reporting added:
CREATE OR REPLACE PROCEDURE ETL_2618A AS
nCheckpoint NUMBER;
BEGIN
nCheckpoint := 1;
EXECUTE IMMEDIATE 'DROP SEQUENCE "METER_REPORTING"."SEQ_2618"';
nCheckpoint := 2;
EXECUTE IMMEDIATE 'CREATE SEQUENCE SEQ_2618';
RETURN;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ETL_2618A failed at checkpoint ' || nCheckpoint ||
' with error ' || SQLCODE || ' : ' || SQLERRM);
RAISE;
END ETL_2618A;
Not tested on animals - you'll be first! :-)