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.