Create FUNCTION in PostgreSQL from a Bash script - bash

I'm trying to create a FUNCTION in my Postgres database from a Bash script. Unfortunately, I cannot get it to work. This is my script:
#!/bin/bash
# Save Postgres command to $POSTGRES_CMD
read -d '' POSTGRES_CMD <<"EOF"
CREATE OR REPLACE FUNCTION truncate_tables(username IN VARCHAR) RETURNS void AS $$
DECLARE
statements CURSOR FOR
SELECT tablename FROM pg_tables
WHERE tableowner = username AND schemaname = 'public';
BEGIN
FOR stmt IN statements LOOP
EXECUTE 'TRUNCATE TABLE ' || quote_ident(stmt.tablename) || ';';
END LOOP;
END;
$$
LANGUAGE plpgsql;
EOF
sudo su - postgres -c "psql -d postgres -U postgres -c \"${POSTGRES_CMD}\""
When I run the script, I get the following error:
ERROR: Syntax error at »20541«
LINE 1: ...N truncate_tables(username IN VARCHAR) RETURNS void AS 20541
So it seems like something is wrong with the $$? How can I create a FUNCTION like in my script in Postgres from a Bash script? Do I have to mask anything?
Edit:
The final, working script (also added create language if it's not registered yet):
#!/bin/bash
sudo su - postgres -c "psql -d postgres -U postgres" << 'EOF'
CREATE LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION truncate_tables(username IN VARCHAR) RETURNS void AS $$
DECLARE
statements CURSOR FOR
SELECT tablename FROM pg_tables
WHERE tableowner = 'username' AND schemaname = 'public';
BEGIN
FOR stmt IN statements LOOP
EXECUTE 'TRUNCATE TABLE ' || quote_ident(stmt.tablename) || ';';
END LOOP;
END;
$$
LANGUAGE plpgsql;

The $$ is replaced by the process id you should escape the $$ thing like this \$\$ or even \\$\\$ as it is escaped two times

Use <<'EOF' to stop bash interpolating the here document.
addition:
You can avoid passing everything through "-c" by using:
sudo su - postgres -c "psql -d postgres -U postgres" <<'EOF'
...
EOF
as stdin should be preserved through sudo and su

if anyone is looking for a version where env variables are passed (DB_USERNAME, DB_SCHEMA) here is:
psql -U dev -d clinical_trial -h db -v ON_ERROR_STOP=1 << EOF
CREATE OR REPLACE FUNCTION truncate_tables() RETURNS void AS \$\$
DECLARE
statements CURSOR FOR
SELECT tablename FROM pg_tables
WHERE tableowner = '$DB_USERNAME' AND schemaname = '$DB_SCHEMA' AND tablename not like 'flyway%';
BEGIN
FOR stmt IN statements LOOP
EXECUTE 'TRUNCATE TABLE $DB_SCHEMA.' || quote_ident(stmt.tablename) || ';';
END LOOP;
END;
\$\$
LANGUAGE plpgsql;
EOF

Related

postgres procedura script does not read input provided by file.sh

I have an Issue running a psql script.
this is the output :
File.SQL:155: ERROR: syntax error at or near ":"
LINE 10: date_temp_Ale INTEGER := :'param_date';
i have a file.sh that is calling the "File.sql" with and Input varible.
here is the line that callsthe sql script:
line in file.sh:
echo le numero des parametres est bien 7
psql -v PGPASSWORD=$MDP -h $IP_MACHINE -f $FIC_REQ_SQL -v param_date=20180101 -U $UTILISATEUR -d $NOM_BDD -p $PORT > $FIC_LOG_EXEC_TMP 2>&1
echo Done in $FIC_LOG_EXEC_TMP
here is the line in the psql script:
line in file.sql"
--- UT 48 PURGE DES TRACES
\set ON_ERROR_STOP on
BEGIN; -- DEBUT DE TRANSACTION
DO $$
DECLARE
num_tra_err_id RECORD;
num_trace_composant RECORD;
num_trace_fonctionnelle RECORD;
num_message_id RECORD;
num_metadonnees_id RECORD;
num_Process_id RECORD;
datediff integer;
date_temp_Ale INTEGER := :'param_date';
Max_days integer := 180;
--date_limite varchar := &1;
BEGIN
i also tried to use the command " --set " in the file.sh but it didn't work.
i found a slution:
the DO statement isolate the inner code from the outer code.
to pass the info we need to store it as psql variable as follow
select set_config('psql.param_date', :param_date, false);
BEGIN; -- DEBUT DE TRANSACTION
DO
$$
DECLARE
limit_date varchar = current_setting('psql.param_date');

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

Chef recipe - not_if with SQL query result

I have in my recipe this block:
bash "Create admin database tables" do
code "mysql -u root -D admindb < /vagrant/files/admin.sql"
not_if shell_out("mysql -u root -s --skip-column-names -e 'SELECT COUNT(DISTINCT table_name)>0 FROM information_schema.columns WHERE table_schema = \"admindb\";'").stdout().chomp()
end
Result of select query is 0 or 1 written to stdout (nothing more). I would like to run shell command only, if result of query will be 0. How can I achieve it?
You just need to use a Ruby equality operator inside the block. Since you said "only if" the output is 0, it would be simpler to do this with only_if rather than not_if:
only_if { shell_out("mysql -u root -s --skip-column-names -e 'SELECT COUNT(DISTINCT table_name)>0 FROM information_schema.columns WHERE table_schema = \"admindb\";'").stdout.chomp == '0' }

Variable won't resolve inside PL-SQL statement

I am trying to pass a SQL command to the SQL*Plus command-line program (Oracle) in my KornShell (ksh) script, but my $MY_VAR variable seems to be failing to resolve. $MY_VAR is a string value.
sqlplus -s << EOF >> $LOG_FILE
$MY_SCHEMA
UPDATE my_table SET run_flag = 'I', start_time = to_char(sysdate, 'DD-Mon-YYYY HH24:MI:SS') WHERE (process_id = '$MY_VAR' AND run_flag != 'F');
COMMIT;
EOF
I can successfully echo out the $MY_VAR variable, and so I can see that the variable is populated, but it does not seem to be resolving when inserting the variable into the SQL command which I am providing as an argument to the SQL*Plus program. The log file for the script simply outputs:
0 rows updated. Commit complete.
The SQL seems to be valid as we can successfully execute the command in SQL Developer (albeit with a hardcoded value for $MY_VAR).
Any ideas on what I am missing here?
Thank you
Try this:
sql="$MY_SCHEMA
UPDATE my_table SET run_flag = 'I', start_time = to_char(sysdate, 'DD-Mon-YYYY HH24:MI:SS') WHERE (process_id = '$MY_VAR' AND run_flag != 'F');
COMMIT;"
print "$sql"
sqlplus -s <<<"$sql" >> $LOG_FILE
Does the sql look correct? If it does and zero rows are updated, your where clause must be selecting zero rows.
Your version of ksh may not have the <<< here-string syntax. In that case:
print "$sql" | sqlplus -s >> $LOG_FILE

How to store multiple output columns returned by stored proc in shell using sqlplus

I have o read values returned by sql stored proc to variables in shell script. I was able to achieve this with single variable but not for two variables.
Oracle stored proc
PROCEDURE get_values (ip_var IN NUMBER,
op_var1 OUT VARCHAR2,
op_var2 OUT VARCHAR2)
IS
BEGIN
SELECT col1, col2
INTO op_var1, op_var2
FROM emp
WHERE emp_id = ip_var;
END;
Calling this proc in unix bash as
sql_status=`sqlplus -silent /nolog << END
whenever sqlerror exit sql.sqlcode
whenever oserror exit -2
set pagesize 0 feedback off verify off heading off echo off scan off serveroutput on
connect $DATABASE
declare
var1 VARCHAR2(20);
var2 VARCHAR2(20);
begin
get_values($input_val, var1, var2);
end;
/
exit;
END`
I need to assign var1 and var2 further in my program. So how can I achieve this?
I know not how to do this interface between PL/SQL (Oracle Server side language) and Unix. You may want to have a jdbc connector for more advanced use.
A solution for your problem is to build an output string directly from the query (or you could use a procedure, and output the result with dbms_output), and process the string in your shell.
Here is how (without procedure):
COL_SEP="%"
RETURN_STRING=`sqlplus -silent /nolog << END
whenever sqlerror exit sql.sqlcode
whenever oserror exit -2
set pagesize 0 feedback off verify off heading off echo off scan off serveroutput on
connect $DATABASE
SELECT col1 ||'${COL_SEP}'|| col2
FROM emp
WHERE emp_id = ip_var;
exit;
END`
echo "RETURN CODE IS: $? "
echo "RETURN STRING : ${RETURN_STRING} "
v1=`echo ${RETURN_STRING} | cut -d${COL_SEP} -f1 `
v2=`echo ${RETURN_STRING} | cut -d${COL_SEP} -f2 `
echo "Variables set: V2:${v2} and V1:${v1}"
And you can do it with dbms_output.put_line in a pl/sql block:
declare
v varchar2(100) := '00';
begin
select 'a'||'${COL_SEP}'||'b' into v from dual;
dbms_output.put_line(v);
end;
/
Hope it helps

Resources