Skip to Main Content
  • Questions
  • Exit UNIX shell script if shell function raises PL/SQL error?

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Art.

Asked: May 05, 2004 - 9:46 am UTC

Last updated: July 07, 2006 - 7:22 pm UTC

Version: 8.1.7

Viewed 10K+ times! This question is

You Asked

Hello, Tom.

I am invoking PL/SQL from within a shell function in a UNIX Korn shell script.

This PL/SQL returns a VARCHAR2 which I in turn use later in the shell script.

If the PL/SQL raises an error, I would like to exit the shell script entirely (from within the shell function), and not just exit the shell function.

Is it possible? FYI, I'm on version 8.1.7.4.

CREATE OR REPLACE PACKAGE pkg
AS
FUNCTION normal_call RETURN VARCHAR2;
FUNCTION error_call RETURN VARCHAR2;
END pkg;
/
CREATE OR REPLACE PACKAGE BODY pkg
AS
FUNCTION normal_call RETURN VARCHAR2 IS
BEGIN
-- Return a random, spelled-out number between one and ten
RETURN (TO_CHAR(TO_DATE(TRUNC(DBMS_RANDOM.VALUE(1,11)),'J'),'jsp'));
END normal_call;

FUNCTION error_call RETURN VARCHAR2 IS
e1 EXCEPTION;
BEGIN
RAISE e1;
RETURN NULL;
EXCEPTION
WHEN e1 THEN
RAISE_APPLICATION_ERROR (-20566
, 'Art''s error has been raised');
END error_call;
END pkg;
/

#!/bin/ksh
# Name = shelfun1.ksh
get_plsql_result()
{
plsql_result=`sqlplus -s scott/tiger <<!
SET SERVEROUTPUT ON FORMAT WRAPPED
SET PAGESIZE 0
SET FEEDBACK OFF
VARIABLE outtext VARCHAR2(250)
BEGIN
:outtext := SUBSTR(scott.pkg.$1,1,250);
EXCEPTION
WHEN OTHERS THEN
RAISE;
END;
/
PRINT :outtext
EXIT
!`
echo $plsql_result
}
echo "$1"
myvar=`get_plsql_result "$1"`
echo "myvar = "$myvar
echo "here I am after the function call"
exit 0

$ shelfun1.ksh normal_call
normal_call
myvar = eight
here I am after the function call
$ shelfun1.ksh error_call
error_call
myvar = BEGIN ajmadhoc.sql abal_00.txt abal_01.txt abal_02.txt abal_03.txt addrchk.sql addrconv1.sql addrconv2.sql
here I am after the function call
$

Oddly, when the error is raised, get_plsql_result returns "BEGIN" plus the results of an "ls" on my home directory....?

So next I tried having the PL/SQL return a string regardless of error status, and then exiting the shell script if this SQLERRM string matched "ORA-nnnnn:". But this approach isn't exiting the shell script, it only exits the shell function and continues on in the shell script.

#!/bin/ksh
# Name = shelfun2.sh
get_plsql_result()
{
plsql_result=`sqlplus -s scott/tiger <<!
SET SERVEROUTPUT ON FORMAT WRAPPED
SET PAGESIZE 0
SET FEEDBACK OFF
VARIABLE outtext VARCHAR2(250)
BEGIN
:outtext := SUBSTR(scott.pkg.$1,1,250);
EXCEPTION
WHEN OTHERS THEN
:outtext := SUBSTR(SQLERRM,1,250);
END;
/
PRINT :outtext
EXIT
!`
if [ `echo "$plsql_result" | grep '^ORA-[0-9]\{5\}:' | wc -l` -eq 1 ]
then
echo "$plsql_result"
exit 1
echo "after the error exit"
else
echo "$plsql_result"
echo "after the normal exit"
fi
}
echo "$1"
myvar=`get_plsql_result "$1"`
echo "myvar = "$myvar
echo "here I am after the function call"
exit 0

$ shelfun2.sh normal_call
normal_call
myvar = two after the normal exit
here I am after the function call
$ shelfun2.sh error_call
error_call
myvar = ORA-20566: Art's error has been raised
here I am after the function call
$

As a workaround, I could perform the regular-expression check for "ORA-nnnnn:" EVERY time AFTER this function is called, but I would really like to encapsulate the "exit-the-shell-script-completely" behavior within the shell function itself, if at all possible.

Can it be done?

Thanks, Tom.

Art.

and Tom said...

use

whenever sqlerror exit N


to exit sqlplus with a non-zero return code and test the return code in your shell script.

Rating

  (6 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

The above solution will not work

Vishal, May 05, 2004 - 12:46 pm UTC

Tom, even if a
whenever sqlerror exit 1
statement is added, sqlplus will still exit with a return code of 0, when it is called from within a shell script as part of a variable assignment, as is it is being done in this case.

Instead, in shelfun2.ksh if a test for the return code is done after the call to the shell function, we can achieve the desired results. See the modified shelfun2.ksh below:

#!/bin/ksh
# Name = shelfun2.sh
get_plsql_result()
{
plsql_result=`sqlplus -s scott/tiger <<!
SET SERVEROUTPUT ON FORMAT WRAPPED
SET PAGESIZE 0
SET FEEDBACK OFF
VARIABLE outtext VARCHAR2(250)
BEGIN
:outtext := SUBSTR(scott.pkg.$1,1,250);
EXCEPTION
WHEN OTHERS THEN
:outtext := SUBSTR(SQLERRM,1,250);
END;
/
PRINT :outtext
EXIT
!`
if [ `echo "$plsql_result" | grep '^ORA-[0-9]{5}:' | wc -l` -eq 1 ]
then
echo "$plsql_result"
exit 1
echo "after the error exit"
else
echo "$plsql_result"
echo "after the normal exit"
fi
}
echo "$1"
myvar=`get_plsql_result "$1"`
if [ $? -ne 0 ] # this is the test for the
then # return code from the call
exit 1 # to the shell function.
fi
echo "myvar = "$myvar
echo "here I am after the function call"
exit 0


I hope Tom doesn't mind.

Christo Kutrovsky, June 18, 2004 - 10:37 pm UTC

First of all let me discourage you from using shell scripts to get result from sqlplus. It is a very unsafe way of gething results, you so it yourself with your listing of files been assigned to your variable (which I will explain in a moment).

I would recommend you to use perl for that. I know Tom dislikes perl, but it is far better then a shell script and sqlplus.

I am not sure what is it that you are trying to do, so I am just going to comment on your current issues.

Quote:
Oddly, when the error is raised, get_plsql_result returns "BEGIN" plus the
results of an "ls" on my home directory....?
[end quote]

The reason why you are gething this is because your are not quoting your echo inside your curly braclets:

echo $plsql_result

And when you get an error the assignment is:
BEGIN
*
ERROR at line 1:
ORA-20566: Art's error has been raised
ORA-06512: at line 5

As you can see there's a star there, which get's expanded to all the files in your current directory.

So if you quote it:

echo "$plsql_result"

Also you need to quote the echo itself later:

from:
echo "myvar = "$myvar

to:
echo "myvar = $myvar"

in double quotes, variable expantion occurs.

And then you get the correct result:
kutro-[~]$ shelfun1.ksh error_call

error_call
myvar = BEGIN
*
ERROR at line 1:
ORA-20566: Art's error has been raised
ORA-06512: at line 5

-----------------------

Now to your problem with the return codes. What Tom was suggesting is using the return code of sqlplus, not the printed result of sqlplus.

So I added this:

!`
echo "sqlplus exit code: $?"
echo "$plsql_result"

And re-run:
kutro-[~]$ shelfun1.ksh error_call
error_call
myvar = sqlplus exit code: 0
BEGIN
...

See the exit code is 0.

Now I added:
SET FEEDBACK OFF
whenever sqlerror exit 1
VARIABLE outtext VARCHAR2(250)

And re-run:
kutro-[~]$ shelfun1.ksh error_call
error_call
myvar = sqlplus exit code: 1
BEGIN

Now if you review the documentation about sqlplus you are going to find out that you can use a bind variable thus have your PL/SQL block deside what is the return code !

Another thing I don't understand why do you call so many sub commands with back quotes. I would do it with a global variable ...

Now, back ontrack to what you wanted, here's the result of the modified script:

kutro-[~]$ shelfun1.ksh normal_call
normal_call
SubFunction exit code: 0
myvar = [six]

here I am after the function call

kutro-[~]$ shelfun1.ksh error_call
error_call
SubFunction exit code: 1

And the script itself:
#!/bin/ksh
# Name = shelfun1.ksh
get_plsql_result()
{
plsql_result=`sqlplus -s kutrovsky/me<<!
SET SERVEROUTPUT ON FORMAT WRAPPED
SET PAGESIZE 0
SET FEEDBACK OFF
whenever sqlerror exit 1
VARIABLE outtext VARCHAR2(250)
BEGIN
:outtext := SUBSTR(pkg.$1,1,250);
EXCEPTION
WHEN OTHERS THEN
RAISE;
END;
/
PRINT :outtext
EXIT
!`

sqlplus_exit_code=$?
if [[ $sqlplus_exit_code -ne 0 ]] ; then
exit $sqlplus_exit_code
fi

#debug echo "sqlplus exit code: $sqlplus_exit_code"
echo "$plsql_result"
}
echo "$1"
myvar="`get_plsql_result "$1"`"
sub_exit_code=$?
echo "SubFunction exit code: $sub_exit_code"
if [[ $sub_exit_code -ne 0 ]]; then
exit $sub_exit_code
fi
echo "myvar = [$myvar]"
echo
echo "here I am after the function call"
exit 0

--- end ---

Note the extra quoting during the function call.



There is nothing wrong with using a shell to call sqlplus

Eric Worthy, July 08, 2004 - 4:28 pm UTC

I have many many scripts in production
that use korn shell scripts with no problem. No
need to use Perl or anything else.

typeset -i LOG_BATCH_ID=`sqlplus -s <<!!
$ORA_USER/$PASSWD@$ORACLE_SID
set heading off
set feedback off
Select tps_log_batch_id_s.nextval from dual;
!!`

ret_code=$?
oerr=`echo $LOG_BATCH_ID | egrep -ic "ora-|pls-|sp2-"`
if [[ $ret_code -ne 0 || $oerr -ne 0 ]]
then
ERROR=SELECT_ERR_BATCH_ID
echo "$(date '+%H:%M:%S') Error getting log batch id \n" >> $LOGFILE
echo $LOG_BATCH_ID >> $LOGFILE
exit_routine (call the exit function where people get paged,etc..)
fi

Tom Kyte
July 08, 2004 - 8:42 pm UTC

(i don't like perl at all personally)

Perl?

A reader, July 09, 2004 - 11:43 pm UTC

Would you elaborate on why you dont like Perl? On what grounds? Technical, philosophical, purity, what?

Thanks

Tom Kyte
July 10, 2004 - 9:15 am UTC

don't like the way it looks, find the code to be very obscure and hard for programmer1 to understand what programmer2 did. most of that is due to lack of comments in the code.

i just don't "like it" - no real technical reason, i don't prevent people from using it, it is just a tool I don't find purpose for myself.

A reader, July 10, 2004 - 10:35 am UTC

Well, it is designed to be a "Swiss Army knife" type of tool, to replace sed, awk, shell, C, C++, etc, etc. So the type of programmers it attracts are typically experienced programmers who are already adept at using the underlying languages. And as such, it is used for quick'n'dirty proof-of-concept implentations that end up lasting for years! Comments, well, as with any language, it is up to the programmer to comment his/her code! The language itself does have a terse and obscure syntax, but really no different than the venerable C language. C can be as terse and hard-to-understand as Perl when used inappropriately.

Personally, I havent found much use for it myself, but I have followed Perl's history and it is quite a fascinating insight into how the open-source model works.

[Actually, Perl is very similar to PHP as far as the "swiss army knife" analogy goes]

Thanks

Tom Kyte
July 10, 2004 - 8:49 pm UTC

Maybe that is the underlying reason I don't like it, to paraphrase:

o it is used by experienced (lone horse) programmers
o for a quick and dirty prototype (hence just whip it together)
o that goes production "as is"
o with nary a shred of documentation or real design behind it.

Inexpensive, yet overpriced in the long run.

whenever sqlerror question

Yoav, July 03, 2006 - 3:32 pm UTC

Hi Tom,
Im using this code in my transportable tablespace shell
script.
I was trying to stop to program flow, when the exec command
fail, or when the select statment return rows.
I tried to use whenever sqlerror , but it didnt help .
Can i get your advice on that ?
Thanks.

SqlError=1
sqlplus -s /NOLOG<<eof
connect / as sysdba
whenever sqlerror exit $SqlError
exec sys.DBMS_TTS.TRANSPORT_SET_CHECK('TEST', TRUE);
select * from TRANSPORT_SET_VIOLATIONS;
whenever sqlerror exit 0
eof
if [ $SqlError != 0 ]
then
echo " ok"
else
echo " Trasport Violation"
fi

Tom Kyte
July 07, 2006 - 7:22 pm UTC

use a plsql block...

exec sys.DBMS_TTS.TRANSPORT_SET_CHECK('TEST', TRUE);

REM you want to exit with something NON-ZERO of course!!!
whenever sqlerror exit 1
begin
for x in ( select * from TRANSPORT_SET_VIOLATIONS )
loop
raise PROGRAM_ERROR;
end loop;
end;
/


More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library