Escaping special character when SQL output to variable in shell script - sql

Trying to assign output from SQL query on an object containing special characters to a variable in shell script.
Running directly on the database:
db2 -x 'select count(*) from <SCHEMA>."/BIC/TEST"'
11000
Yet when I include this in script I need to use double quotes as I am using variables passed into the sql. Using single quotes
Output=$(db2 -x 'select count(*) from ${_SCHEMA}."/BIC/TEST"')
echo -e "$Output"
Results in:
SQL20521N Error occurred processing a conditional compilation directive near
"_". Reason code="7". SQLSTATE=428HV
When I use double quotes I hit:
SQL0104N An unexpected token "'/BIC/TEST'" was found following "ount(*)
Tried to escape the double quotes using another set of double quotes:
db2 -x 'select count(*) from ${_SCHEMA}.""/BIC/TEST""'
But this doesn't seem to work in script. It works for tables where there is no special characters/requirement to encase in quotations.
Any help is appreciated.

The code below works fine for me, notice the escaped quotes. If it fails for you, you need to give more details of your DB2-version and the DB2-server operating system platform.
#!/bin/ksh
db2 connect to sample
(($? > 0 )) && print "Failed to connect to database" && exit 1
db2 -o- "drop table \"/bin/test\" "
db2 -v "create table \"/bin/test\"(a integer)"
(($? > 0 )) && print "Create table failed" && exit 1
db2 -v "insert into \"/bin/test\"(a) values(1),(2),(3),(4)"
(($? > 0 )) && print "insert rows failed" && exit 1
db2 -v describe table \"/bin/test\"
typeset -i count_rows=$(db2 -x "select count(*) from \"/bin/test\"" )
(($? > 0 )) && print "query count rows failed" && exit 1
print "\nRow count is: ${count_rows}\n"
db2 -o- connect reset

Related

integer expression expected shell scripting - BASH

I am trying to get the LAG between Primary & Standby database using the below shell script. The query works fine returning the values "DATABASE IS OUTOFSYNC" or "DATABASE IS INSYNC" for an instance that has 1 Node which returns a single value, but I get an error "[: 0 1: integer expression expected" for an instance that has two Nodes which returns two values for the LAG on the first Node and the Second Node.
So here is the code:
#!/bin/bash
get_status=$(sqlplus -s "/as sysdba" <<EOF
set pagesize 0 feedback off verify off heading off echo off;
SELECT prim.seq - tgt.seq seq_gap
FROM
(
SELECT thread#, MAX(sequence#) seq, MAX(completion_time) tm
FROM
v\$archived_log
GROUP BY
thread#
)
prim,
(
SELECT thread#, MAX(sequence#) seq, MAX(completion_time) tm
FROM
v\$archived_log
WHERE
dest_id IN
(
SELECT
dest_id
FROM
v\$archive_dest
WHERE
target = 'STANDBY'
)
AND
applied = 'YES'
GROUP BY
thread#
)
tgt
WHERE
prim.thread# = tgt.thread#;
exit;
EOF
)
if [ "$get_status" -ge 5 ]; then
echo "DATABASE IS OUTOFSYNC"
else
echo "DATABASE IS INSYNC"
fi
Is there a better way to write this script?
After adding typeset -p get_status after the query and before the if I get the below results:
declare -- get_status=" 1
0"
./dgtest2.sh: line 41: [: 1
0: integer expression expected
DATABASE IS INSYNC
The query is returning more than one value/string (for 2 nodes or threads) as shown in picture/screenshot and it seems like my script is only coded to address a single value/string generated by the query.
enter image description here
Is there away to modify the script to address multiple values/strings generated by the query
The logic should be if all values returned are -ge 5 it should report "DATABASE IS OUTOFSYNC" else "DATABASE IS INSYNC" for all values returned are -lt 5.
The logic for one value -lt 5 and one value -ge 5 would not suffice as the values constantly change on the database.
Any values from 0 - 4 that the database returns whether from both Nodes should report as "DATABASE IS INSYNC" and any value from 5 upwards that the database returns whether from both Nodes should report as "DATABASE IS OUTOFSYNC".
One idea would be to capture the status values (returned by the sqlplus script) into an array and then loop through the array testing said status values.
Instead of:
variable=$(sqlplus ...)
We want:
variable=( $(sqlplus ...) )
For OP's current scripting, with a name change for the variable, we will replace this:
get_status=$(sqlplus -s "/as sysdba" <<EOF
set pagesize 0 feedback off verify off heading off echo off;
SELECT prim.seq - tgt.seq seq_gap
...
exit;
EOF
)
With this:
status_array=( $(sqlplus -s "/as sysdba" <<EOF
set pagesize 0 feedback off verify off heading off echo off;
SELECT prim.seq - tgt.seq seq_gap
...
exit;
EOF
) )
One idea for the follow-on logic testing:
default database status is INSYNC
if any status values are -ge 5 then set database status to OUTOFSYNC
The code for this looks like:
db_status='INSYNC'
for status in "${status_array[#]}"
do
[[ "${status}" -ge 5 ]] && db_status='OUTOFSYNC' && break
done
echo "DATABASE IS ${db_status}"
I'm not setup to run the sqlplus script but I should be able to simulate the results with the following array assignments:
status_array=(1)
status_array=(7)
status_array=(0 1)
status_array=(5 7)
status_array=(5 3)
Running our code for each of these array assignments gives us:
##################### status_array=(1)
DATABASE is INSYNC
##################### status_array=(7)
DATABASE is OUTOFSYNC
##################### status_array=(0 1)
DATABASE is INSYNC
##################### status_array=(5 7)
DATABASE is OUTOFSYNC
##################### status_array=(5 3)
DATABASE is OUTOFSYNC

Don't understand where is syntax error ? PostgreSQL 11

PostgreSQL version : 11.1
Platform : OSX Mojave 10.14.1
That's my SQL code:
COPY (select nom,prenom,num_carte,pdv_carte,email,date_naissance from compte where num_carte != '' order by id_compte) TO :export_file WITH DELIMITER AS ';' CSV FORCE QUOTE * ENCODING 'UTF-8';
That line is in a .sql file called by shell script like this :
psql --dbname=test -U postgres --set adresses=$DATA_ADRESSE --set export_file="$EXPORT_FILE" --file=$ANO_SQL 1>>$ANO_LOG
With EXPORT_FILE variable declared like that :
export EXPORT_FILE=‎⁨"'export_for_fid.csv'"
Tried many solutions but none worked, always the same syntax error:
ERROR: syntax error at or near "‎⁨"
LINE 1: ...where num_carte != '' order by id_compte) TO ‎⁨'export_for_fid.csv' WITH D...
^
Instead of using --file and --set arguments, you could use a here-document in your shell script: (NOTE: I replaced the COPY by a \COPY )
#!/bin/sh
EXPORT_FILE="export_for_fid.csv"
DB_NAME="test"
psql --dbname=${DB_NAME} -U postgres <<OMG
\COPY (select nom,prenom,num_carte,pdv_carte,email,date_naissance from compte where num_carte <> '' order by id_compte) TO '${EXPORT_FILE}' WITH DELIMITER AS ';' CSV FORCE QUOTE * ENCODING 'UTF-8';
OMG
#eof

variable passed to sql from shell is not working

My code is:
#!/bin/sh
cat tmp_ts.log | awk ' {print $8}'
lookup=$8
sqlplus -s "sys/Orcl1234 as sysdba" << EOF
SELECT tablespace_name FROM dba_tablespaces WHERE tablespace_name='$lookup';
exit;
EOF
and my output is:
IAM_OIM
no rows selected
In this variable lookup I have passed to select statement but it's not working.
My end result should be with select statement. See below the output of select query:
See below:
My end result should be this but that variable is not working in select statement.
#!/bin/sh
lookup="$(awk '/tablespace/{print $8;exit}' tmp_ts.log)"
echo "Querying database with lookup = $lookup"
sqlplus -s "sys/Orcl1234 as sysdba" <<EOF
SELECT tablespace_name FROM dba_tablespaces WHERE tablespace_name='$lookup';
exit;
EOF
You have to use awk's output to set lookup. The shell knows nothing about the $8 which was set in awk. Also, I have ensured that awk exits after the first matching line, so that there is no risk of returning multiple values, or simply empty lines as it did in your version.
You can fill lookup with a command like awk, sed or cut.
lookup=$(cut -d" " -f8 tmp_ts.log)
You should add some checks, like #Dario did (with an exit after the first match and only converting lines with tablespace, but what to do when no lines match?).
When you don't add the checks you can skip setting the $lookup:
sqlplus -s "sys/Orcl1234 as sysdba" << EOF
SELECT tablespace_name FROM dba_tablespaces
WHERE tablespace_name='$(sed 's/.*tablespace- //' tmp_ts.log)';
exit;
EOF

Assign output of sql query to a variable in unix

I am using the following simple UNIX script to assign the output to a variable.
count=`sqlplus -s ${DB_USER}/${DB_PASS}#${DB_INST} << END
SELECT COUNT(column_name) from table_name;
END`
echo $count
But I am getting the following error on executing:
SP2-0042: unknown command "END" - rest of line ignored.
count=`sqlplus -s ${DB_USER}/${DB_PASS}#${DB_INST} << END
When I tried to execute the above statement in putty, it was saying as "bad substitution"
So I am using UNIX script to assign the output to a variable.
count=sqlplus DB_USER/DB_PASS << END
SELECT VERSION_NUMBER from GA_PERIODIC_REFRESH where MODULE_NAME in 'RoaminfoService';
exit;
END
echo $count
You need exit also:
count=`sqlplus -s ${DB_USER}/${DB_PASS}#${DB_INST} <<END
set pages 0 echo off feed off
SELECT COUNT(column_name)
exit;
END`

simple Shell Script - howto do the same in PL/SQL

I have a simple shell script with SQL code which does:
generate with SQL*Plus (SQL statement) a batch file
checks if output from SQL*Plus more than 400 lines (if more than 400 lines exit and writes mail to Operations team)
if less than 400 lines SQL*Plus output, executes the batch file automatically
This script works very well. I wish to write the same script with PL/SQL (without Shell code). Is this possible? Can you provide me the code (I am in process of learning PL/SQL).
Database is Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 on Solaris.
#!/bin/ksh
. /opt/db/scripts/setpath.sh
generate_batch ()
{
sqlplus -S $DBUSER/$DBPASSWD#$ORACLE_SID <<EOF > /opt/db/scripts/tools/delete_connection/batchrun/batchrun.$(/bin/date '+%d%m%Y.%Hh')
set echo Off
set term On
set pages 0
set head off
set ver off
set feed off
set trims on
set linesize 20000
WITH data
AS (SELECT user_id,
jc_name,
upd_time,
RANK () OVER (PARTITION BY user_id ORDER BY upd_time ASC)
rk
FROM user_jc
WHERE user_id IN ( SELECT user_id
FROM user_jc
WHERE JC_NAME LIKE 'CFF\_S\_%' ESCAPE '\'
GROUP BY user_id
HAVING COUNT (user_id) > 1)
AND JC_NAME LIKE 'CFF\_S\_%' ESCAPE '\')
SELECT 'DISCONNECT ent_user FROM job_code WITH user_id = "'
|| user_id
|| '", jc_name = "'
|| jc_name
|| '";'
FROM data
WHERE rk = 1;
exit
EOF
}
sanity_check ()
{
line_nr=$(wc -l /opt/db/scripts/tools/delete_connection/batchrun/batchrun.$(/bin/date '+%d%m%Y.%Hh') | awk ' { print $1 } ')
if [ $line_nr -gt 400 ]; then
(cat /opt/db/scripts/tools/delete_connection/mail_body.txt) | mailx -s "Alert: please manually execute /opt/db/scripts/tools/delete_connection/batchrun/batchrun.$DATE" -r test#example.com test2#example.com
exit 1
fi
}
run_batch ()
{
/opt/bmchome/bin/ess batchrun -A -i /opt/db/scripts/tools/delete_connection/batchrun/batchrun.$(/bin/date '+%d%m%Y.%Hh')
}
generate_batch && sanity_check && run_batch
In PL/SQL, I'd do it the other way round:
Count number of connections that match your query
If result > 400 send email
Else generate the disconnection statements, probably with ALTER SYSTEM DISCONNECT SESSION...
I don't know your requirement of course, but could it be solved with resource profiles to limit user connections?
CREATE PROFILE myprofile LIMIT SESSIONS_PER_USER = 1;
ALTER USER myuser PROFILE myprofile;