2

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;
1
  • your function doesn't take care of arguments passed as you hardcoded them. do you have a solution for the case when you can pass these args? Commented Jun 13, 2022 at 23:02

3 Answers 3

9

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

Sign up to request clarification or add additional context in comments.

2 Comments

It actually works when escaping the dollar signs twice. Didn't knew about that. Thanks!
The araqnid answer is far better than mine
4

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

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.