Error Handling of SQLPlus from Bash - not working - oracle

I have read every relevant link on this issue and similar questions/answers, preveland answer is to first set whenever SQLERROR EXIT SQL.SQLCODE;
Only then do the query, and then inspect the SQL Plus return code using: ERRORCODE=$?
Here is a sample script:
GetAmountOfChunks()
{
export CHUNK_AMOUNT=`sqlplus -s $CONSTR<<SQL
set heading off;
set trim on;
set feed off;
whenever SQLERROR EXIT SQL.SQLCODE;
select 1/0 from dual;
--SELECT COUNT(*) FROM CNV_CHUNKS_PROC_STATUS;
/
SQL`
When ran it debug mode, it gives:
++ sqlplus -s USER/PASS#HOST/DB
+ export 'CHUNK_AMOUNT= select 1/0 from dual
*
ERROR at line 1:
ORA-01476: divisor is equal to zero'
+ CHUNK_AMOUNT=' select 1/0 from dual
*
ERROR at line 1:
ORA-01476: divisor is equal to zero'
+ ERRORCODE=0
+ '[' 0 -ne 0 ']'
As you can see, returned code is 0!
I expected if not 1476, then at least 196 (right most 8 bytes), but not 0 which indicates success!
Please help...
Thanks.

Your ERRORCODE is being set to zero because that's the exit code from the subshell you're running SQL*Plus is, viq the backticks. The exit code of the SQL*Plus process is 196 but you aren't capturing that, and it's not that easy to do that within a heredoc. The stdout from the process - which is what you are capturing - is not that exit code, it's the query and error message being printed. And even if you could capture it, I'm not sure how you'd distinguish between the 196 coming from an error, or from your actual query.
You could do something like running the query in a block that hides the error and prints either a default value or the actual calculated value, or only look at the last line of output and try to interpret that; but you'll still be fighting against it showing the command being run. SQL*Plus has set echo off but that doesn't do anything with an interactive session, which this still is with input redirection.
Another way to go is to create a script file and temporarily store the output:
echo "
set pages 0
set trim on
set feed off
set echo off
whenever SQLERROR EXIT FAILURE
select 1/0 from dual;
--SELECT COUNT(*) FROM CNV_CHUNKS_PROC_STATUS;
exit 0;
" > /tmp/GetAmountOfChunks_$$.sql
sqlplus -s -l $CONSTR #/tmp/GetAmountOfChunks_$$.sql > /tmp/GetAmountOfChunks_$$.out
if [[ $? -eq 0 ]]; then
export CHUNK_AMOUNT=`cat /tmp/GetAmountOfChunks_$$.out`
else
# whatever you want to do on error; show output file? set default?
cat /tmp/GetAmountOfChunks_$$.out
fi
rm -f /tmp/GetAmountOfChunks_$$.sql /tmp/GetAmountOfChunks_$$.out
This creates a (process-specific) .sql file; executes that write the output (minus the statement, via set echo off) to a .out file; checks the SQL*Plus exit code; and if that is zero gets the result from the file.
As you hinted relying on SQL.SQLCODE to detect an error from your shell script is dangerous as you could get an error that wraps to zero, so I've used the generic FAILURE. If you need the real error code you can get it from the output file.
Another approach using a PL/SQL block:
set -f
CHUNK_AMOUNT=`sqlplus -s $CONSTR <<SQL
set heading off;
set trim on;
set feed off;
whenever SQLERROR EXIT FAILURE;
set serveroutput on;
declare
chunk_amount number;
begin
select 1/0 into chunk_amount from dual;
--SELECT COUNT(*) INTO chunk_amount FROM CNV_CHUNKS_PROC_STATUS;
dbms_output.put_line(chunk_amount);
exception
when others then
dbms_output.put_line(sqlcode);
end;
/
exit 0
SQL
exit $?`
ERRORCODE=$?
If the PL/SQL block runs then ERRORCODE will be zero and CHUNK_AMOUNT will be the calculated value if it's successful, or the SQL code if it throws an exception; since that will be negative (-1476 in your example) you can test that to see if it's expected, if you're only expecting positive values.
If the block can't run because of a syntax error or invalid credentials (notice the -l flag I snuck in) then ERRORCODE will be 1 and CHUNK_AMOUNT will have the error text, e.g. ERROR: ORA-12154: TNS:could not resolve the connect identifier... or whatever actually went wrong. The set -f stops a * in the error message being expanded into a file list from the current directory.
Or even more simply and closer to your original:
set -f
CHUNK_AMOUNT=`sqlplus -s $CONSTR <<SQL
set heading off;
set trim on;
set feed off;
whenever SQLERROR EXIT FAILURE;
select 1/0 from dual;
--SELECT COUNT(*) FROM CNV_CHUNKS_PROC_STATUS;
exit 0
SQL
exit $?`
ERRORCODE=$?
and now ERRORCODE is 0 on success and CHUNK_AMOUNT has the calculated value on any error ERRORCODE is 1, you can test that directly, and the actual error is always in CHUNK_AMOUNT - but only as a string, you don't get -1476 this way.

Related

Handle Not Found Exceptions in sqlplus

I am trying to invoke sqlplus command from a shell script and then trying to
check for success/failure using the exit status. However this does not work fo not found scenario. I need to get the exit code of non zero when there are no rows deleted for a given input.
sqlplus -s $DB_CONNECTION << EOF
set heading off pages 0 echo off feedback off
WHENEVER SQLERROR EXIT SQL.SQLCODE
BEGIN
DELETE FROM config where Id_ = '1345';
IF SQL%ROWCOUNT = 0 THEN
RAISE NO_DATA_FOUND;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
EXIT 100;
END;
EOF
A PL/SQL EXIT statement only works inside a LOOP . Check this: EXIT Statement. It is not equivalent to shell's exit <exitcode> or SQL* Plus' exit.So, your PL/SQL block won't compile at all.
Moreover, you cannot ensure that Shell will capture the same exit code as that of the Oracle's SQLCODE for exceptions like no_data_found. Since you already have WHENEVER SQLERROR, you may not include the EXCEPTION block at all and simply check for non-zero exit code.
sqlplus -s $DB_CONNECTION << EOF
set heading off pages 0 echo off feedback off
WHENEVER SQLERROR EXIT SQL.SQLCODE
BEGIN
DELETE FROM config where Id_ = '1345';
IF SQL%ROWCOUNT = 0 THEN
RAISE NO_DATA_FOUND;
END IF;
END;
EOF
exit_code=$?
if [ $exit_code != 0 ]
then
echo "No rows deleted for the given input"
fi

KSH Script using SQLPlus will not commit

function SQL_Command
{
result=`sqlplus -s /nolog <<!EOF
connect ********
whenever sqlerror exit failure
set pagesize 0
set feedback off
$1
$2
exit 0
!EOF`
}
So I have this function (above) and a few lines of codes beneath which are mostly inserts and updates, but anyhow they are not committed after execution.
I tried to add commit; but still it will not like literally be committed :c
function is used like this: SQL_Command "update ...." "commit;"
well at least that's what I've tried so far... anyone got any ideas :?
Thanks in advance!
brgds
I mean this: the heredoc terminator must not have any leading or trailing whitespace or any other characters*
function SQL_Command
{
result=$(sqlplus -s /nolog <<!EOF
connect ********
whenever sqlerror exit failure
set pagesize 0
set feedback off
$1
$2
exit 0
!EOF
)
}
*with one exception that you can read about in the bash manual.

Error while connecting the sqlplus from shell script

REQ_OUTPUT1=`echo $XXOLA_TOP/log/Inv/xxola_inv_item_conv_int`
echo Connecting in sqlplus
sqlplus -s $1#$TWO_TASK <<+ > $REQ_OUTPUT1
SET VERIFY OFF
WHENEVER SQLERROR EXIT FAILURE ROLLBACK;
SET LINESIZE 500;
SET SERVEROUTPUT ON SIZE 1000000;
SELECT 1 FROM DUAL;
exit;
Above is my code to connect sqlplus from shell program.
But when i am running this i am getting below error..
line 36: warning: here-document at line 29 delimited by end-of-file (wanted `+')
You are not ending the here document properly. Don't use symbols like + for here document delimiters. Use proper meaningful strings.
REQ_OUTPUT1=`echo $XXOLA_TOP/log/Inv/xxola_inv_item_conv_int`
echo Connecting in sqlplus
sqlplus -s $1#$TWO_TASK <<INP > $REQ_OUTPUT1
SET VERIFY OFF
WHENEVER SQLERROR EXIT FAILURE ROLLBACK;
SET LINESIZE 500;
SET SERVEROUTPUT ON SIZE 1000000;
SELECT 1 FROM DUAL;
exit;
INP

How to fetch more than one column value from oracle select query to shell variable

I am trying to fetch a row with more than one column value to different shell variables. Infact I found that at a time all the column values can be stored to single shell variable. But how can I put those column values to seperate shell variables. Below is an example I am trying for time being
function sqlQuery {
sqlplus -S shiyas/********* <<'EOF'
set heading OFF termout ON trimout ON feedback OFF
set pagesize 0
SELECT name,open_mode from v$database;
EOF
}
OUTPUT="$( sqlQuery )"
echo $OUTPUT
Here I am getting the output as
ORCL READ WRITE
But my requirement is column values ORCL, READ WRITE should get assigned to different shell variable.
I tried the below of parsing.
echo "$OUTPUT" | while read name open_mode
but it was throwing unexpected end of file error.
-bash-3.2$ sh call_sql_col_val_1.sh
ORCL READ WRITE
call_sql_col_val_1.sh: line 18: syntax error: unexpected end of file
Please let me know what concept I can use to fetch a single row column values to different shell variables.
I do this via eval myself:
oracle#******:/*****> cat test.sh
#!/bin/bash
function sqlQuery {
sqlplus -S / as sysdba <<'EOF'
set heading OFF termout ON trimout ON feedback OFF
set pagesize 0
SELECT name,open_mode from v$database;
EOF
}
eval x=(`sqlQuery`)
NAME=${x[0]}
OPEN_MODE="${x[1]} ${x[2]}"
echo NAME IS $NAME
echo OPEN_MODE IS $OPEN_MODE
So we are running the same function you have above, passing it into x and running it through eval to handle the delimitation. Then you have an array and call call is as such: x[0] for the first item, for example.
Output is:
oracle#******:/******> sh test.sh
NAME IS ******
OPEN_MODE IS READ WRITE

How to return error code in case plsql compilation error from sqlplus

I use "WHENEVER SQLERROR EXIT SQL.SQLCODE ROLLBACK;" in my plsql scripts to use them in shell scripts. This works fine:
echo exit | sqlplus user/pass#XE #normal.sql && echo "boo"
Executes the script and prints "boo"
This works fine too:
echo exit | sqlplus user/pass#XE #bad.sql && echo "boo"
"boo" is not printed.
However in case bad is:
WHENEVER SQLERROR EXIT SQL.SQLCODE ROLLBACK;
create or replace
PACKAGE TESTING
IS
function boo (co_id number) return varchar2;
END;
/
create or replace
PACKAGE BODY TESTING
is
end;
Which is obviously wrong - no error code is returned and "boo" is printed. How can I return plsqsl compilation error codes from sqlplus scripts?
You will need to parse them from the output. Unix error codes are in the range 0 to 255, and theres all sorts of masks and signal stuff hidden in there. So you cannot record oracle error numbers in unix error codes.
So basically you need to make your sql scripts include the show errors statement. But you do NOT want the WHENEVER statement in there because that will error before the errors are printed. e.g. bad.sql will be
create or replace
PACKAGE TESTING
IS
function boo (co_id number) return varchar2;
END;
/
show errors
create or replace
PACKAGE BODY TESTING
is
end;
/
show errors
Then your shell script should be something like:
ERR_OUT="$( sqlplus rdbds/rdbds#XE < bad.sql | egrep '^(ORA|PLS)-' )"
if [ -n "$ERR_OUT" ]
then
echo "Errors in SQL:"
echo "$ERR_OUT"
else
echo boo
fi

Resources