Call PL/SQL block in unix script - oracle

I am trying to call pl/sql block in unix script using sqlplus. I just tried to print a statement but nothing is getting printed and I am not getting any error as well.
Result=`sqlplus -s $TgtUsrID/$TgtPswd#$TgtServer <<eof
whenever sqlerror exit sql.sqlcode;
SET SERVEROUTPUT ON;
BEGIN
DBMS_OUTPUT.PUT_LINE('Hello World!');
END;
eof
`
current_time=`date`
echo " Script execution finished at $current_time"

Just need to prepend the variable(Result) with the dollar operator after the last EOF
along with an echo command such as
Result=`sqlplus -S /nolog << EOF
conn $TgtUsrID/$TgtPswd#$TgtServer
whenever sqlerror exit sql.sqlcode
set feedback off
SET SERVEROUTPUT ON;
BEGIN
DBMS_OUTPUT.PUT_LINE('Hello World!');
END;
/
EOF`
echo $Result
echo Script execution finished at $(date)
would yield such a result
Hello World!
Script execution finished at Tue Feb 14 00:15:51 +03 2021
where quotes for the description after the last echo is redundant, and prefer using connection with /nolog as being securer, since the password of the schema would be unveiled whenever ps -ef command issued in the current case.

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

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 call oracle function from a Korn Shell Script?

I have a user defined oracle function that returns a number that can be greater than 255. I call that function from a shell script using sql plus, it returns the value, for eg 296, but the scripts accepts it as 40, which is because the script can only accept return codes from 0-255.
This is how i am currently receiving the value
echo ${PASSWORD} | sqlplus ${USERNAME}#${SID} #$SQL getnumber.sql $PARAM> ${LOG}
number=$?
getnumber.sql has
set serveroutput on size 100
VARIABLE rc NUMBER;
call function_get_number('&2') into :rc;
print rc;
exit :rc;
How can i preserve the return value? Should i write it to a file? if so how/where
Script getnumber.sh:
cat << EOF | sqlplus /S /nolog >${LOG}
conn ${USERNAME}/${PASSWORD}#${SID}
set serveroutput on size 100
VARIABLE rc NUMBER;
exec :rc := function_get_number('$PARAM');
SELECT 'RETVAL:' || :rc || ':' theval FROM dual;
EOF
RC=$( grep '^RETVAL:' ${LOG} | cutr -d":" -f2 )
echo $RC

Error Handling of SQLPlus from Bash - not working

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.

BASH commands inbetween SQLPlus code or how to count the time used to run different SQLplus commands?

For performance testing purposes - I need to count duration of SQLPlus query. Its not so simple as it seems to be.
I have the code here:
VARIABLE RC REFCURSOR;
EXEC :RC := $aktuellesBericht;
SPOOL ${BERICHT}_${configArray[0]}.DATA
PRINT RC;
SPOOL OFF
It's just a part of the whole command. The whole command looks like this:
sqlplus -s /nolog > /dev/null 2>&1 <<EOF
CONNECT USER/PASSWORD#DATABASE
#A ton of unimportant SET and CLEAR commands goes here#
VARIABLE RC REFCURSOR;
EXEC :RC := $aktuellesBericht;
SPOOL ${BERICHT}_${configArray[0]}.DATA
PRINT RC;
SPOOL OFF
QUIT
EOF
I need to count time of EXEC :RC := $aktuellesBericht; and PRINT RC; separately. Is this even possibe? Can I count it somehow? Let me use some pseudocode showing what I want to do:
execstart = date +%s%N
EXEC :RC := $aktuellesBericht;
execend = date +%s%N
SPOOL ${BERICHT}_${configArray[0]}.DATA
printstart = date +%s%N
PRINT RC;
printend = date +%s%N
SPOOL OFF
exectime=$(( execend - execstart ))
printtime=$(( printend - printstart ))
echo $exectime;
echo $printtime;
bash has a builtin time command which executes a command then print various times :
If the time reserved word precedes a pipeline, the elapsed as well as
user and system time consumed by its execution are reported when the
pipeline terminates. The TIMEFORMAT variable may be set to a format string that specifies how the timing
information should be displayed;
# only the real time
TIMEFORMAT="This was done in : %R seconds"
time EXEC :RC := $aktuellesBericht;

Resources