10

I have some scripts that get run often, always from within a connected SQLPlus session.

I need a way to exit the script when an error occurs, without disconnecting or exiting SQLPlus itself. 100% of the time, when an error occurs, the connected DBA will need to issue one or more commands into the session. 100% of the time, when an error occurs, there are other SQLPlus statements (and thus must be outside of a BEGIN..END;) later on in the script that must not be executed or serious problems could arise.

NOTE: If you suggest WHENEVER SQLERROR EXIT then you didn't read the above text. That will disconnect and exit SQLPlus in addition to the script, which is not acceptable behavior.

2
  • 1
    Of of interest why don't you split your scripts up into a shell / batch script and use whenever sqlerror exit and then the shell script error-handling to decide whether to continue? Commented Jun 26, 2012 at 8:32
  • Because there are a bunch of manual stuff that they'll want to do before and/or after the script runs, including running other scripts, that are specific to the situation / environment / data / etc. involved. This is primarily for fixing things in production, and constantly being kicked out / having to reconnect is worse than annoying when your site is down. They can't be broken up into multiple scripts because the SQLPlus commands are making use of the output from the PL/SQL commands. Commented Jun 27, 2012 at 20:31

4 Answers 4

9

I've found an interesting idea here which, when combined with spencer7593's answer, will get me selective sub-script calling, to which I can pass the PL/SQL output values. To wit:

VAR continue number;
EXEC :continue := 1;
BEGIN
   SELECT some_bool_test() INTO :continue FROM dual;
END;

SET termout OFF
COLUMN script_name NEW_VALUE v_script_name
SELECT decode(:continue, 1, 'run_stuff.sql', 'skip.sql') script_name FROM dual;
SET termout ON

@&v_script_name :some_other_values

Where skip.sql is an empty text file.
 

UPDATE: I've moved most of this into a RUN.SQL file, where I pass in the boolean (0 or 1) as &1, the script name to call on success as &2, and then any other expected parameters to pass to the called script. Thus, it ends up looking something like this:

VAR continue number;
EXEC :continue := 1;
BEGIN
   SELECT some_bool_test() INTO :continue FROM dual;
END;

@run.sql :continue 'run_stuff.sql' :some_other_values
Sign up to request clarification or add additional context in comments.

2 Comments

Sad that a database that costs as much as Oracle makes you jump through so many hoops. Isn't it?
Sorry, downvoted by mistake. Please edit so that I can revert my vote.
4

It's not possible.

SQLPlus doesn't provide that level of control over the execution of a script.

Obviously, you'd need to AVOID using the WHENEVER SQLERROR EXIT ... command.

It's possible to gain conditional control over which SQL statements do or do not get executed as a result of raised exceptions (errors) using PL/SQL. But that doesn't address SQLPlus commands (which cannot be executed from within a PL/SQL block.)

DECLARE
  lb_continue BOOLEAN;
BEGIN
  lb_continue := TRUE;
  BEGIN 

    sql statement

  EXCEPTION
    WHEN OTHERS THEN
      lb_continue = FALSE;
  END;
  IF lb_continue THEN
  BEGIN

    sql statements

  EXCEPTION
    WHEN OTHERS THEN
      lb_continue := FALSE;
  END; 
END;

Of course, that approach has it's own limitations and issues. Any DDL statements would need to be called dynamically; the easiest way to do that is an EXECUTE IMMEDIATE statement.

The biggest issue (in your case) is that it's not possible to execute SQLPlus commands from inside a PL/SQL block.

2 Comments

I'm just confused as to why this wasn't implemented as the default behavior when you're running SQLPlus as a command-line (rather than as a one-shot connect-script_run-disconnect). I can't think of any situation where I would want any other behavior than just returning to the SQLPLus command prompt.
I'm not sure what you mean by the default behaviour. Do you mean why isn't there a 'go to end of current script' command in SQL*Plus?
1

You can't exit the script and stay in SQL*Plus, but you can stop executing things. It isn't pretty, but assuming you can modify the script to add the control flow then you can just about do this with bind variable.

set serveroutput on

var flag char;
exec :flag := 'Y';

begin
    if :flag != 'Y' then
        raise program_error;
    end if;
    dbms_output.put_line('Doing some work');
    /* Check for some error condition */
    if 0 != 1 then
        raise program_error;
    end if;
    /* Only reach this if earlier statements didn't fail
     * but could wrap in another flag check if needed */
    dbms_output.put_line('Doing some more work');
    exception
        when program_error then
            dbms_output.put_line(sqlerrm);
            :flag := 'N';
        when others then
            /* Real exception handling, obviously */
            dbms_output.put_line(sqlerrm);    
            :flag := 'N';
end;
/

-- DML only does anything if flag stayed Y
select sysdate from dual
where :flag = 'Y';

-- Optional status message at the end of the script, for DBA info
set feedback off
set head off
select 'Something went wrong' from dual where :flag != 'Y';
set feedback on
set head on

When executed:

SQL> @script

PL/SQL procedure successfully completed.

Doing some work
ORA-06501: PL/SQL: program error

PL/SQL procedure successfully completed.


no rows selected


Something went wrong
SQL> 

Any PL/SQL blocks in the script can check the flag status at the start, and raise program_error (just as a handy pre-defined exception) to jump back out. Anything that errors inside a PL/SQL block can update the bind variable flag, either directly or in an exception handler. And any non-PL/SQL DML can have an additional where clause to check the flag status, so if it's been set to N by the time that statement is reached, no work is done. (For an insert I guess that would mean not using the values form).

What this can't do is deal with any errors from plain SQL statements, but I'm not sure if that's an issue. If it is then those might need to be changed to dynamic SQL inside a PL/SQL block.

Comments

-1

I know its old, but these two instructions at the very begining of the SQL script do the work:

WHENEVER SQLERROR EXIT FAILURE ROLLBACK

WHENEVER OSERROR EXIT FAILURE ROLLBACK

1 Comment

Those will disconnect.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.