7

Here's what I would like to do:

\set values foo,bar,baz

DO $$
DECLARE
    value  TEXT;
    values TEXT[] := string_to_array(:'values', ',');
BEGIN
    FOREACH value IN ARRAY values LOOP
        raise notice 'v: %', value;
    END LOOP;
END $$ LANGUAGE plpgsql;

Which results in the following error:

ERROR:  syntax error at or near ":"
SELECT string_to_array(:'values', ',') INTO values...
                       ^

Here's the solution I have currently, but it feels hacky:

\set values foo,bar,baz

PREPARE get_values AS SELECT string_to_array(:'values', ',');

DO $$
DECLARE
    value  TEXT;
    values TEXT[];
BEGIN
    EXECUTE 'EXECUTE get_values' INTO values;

    FOREACH value IN ARRAY values LOOP
        raise notice 'v: %', value;
    END LOOP;
END $$ LANGUAGE plpgsql;
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228

2 Answers2

3

Answer

DO expects a string literal with plpgsql code. Symbols are not substituted inside strings in psql.
You could concatenate the whole string into a psql variable and then execute it.

Pretty multi-line format is not possible, because (per documentation):

But in any case, the arguments of a meta-command cannot continue beyond the end of the line.

Simple example:

test=# \set value foo
test=# \set do 'BEGIN\n   RAISE NOTICE ''v: %'', ' :'value' ';\nEND'
test=# DO :'do';
NOTICE:  v: foo

Replace line breaks with \n (or remove them if you don't care for pretty format). Based on this adapted code:

DO
'
DECLARE
   _val  text;
   _vals text[] := string_to_array(>>values<<, '','');
BEGIN
   FOREACH _val IN ARRAY _vals
   LOOP
     RAISE NOTICE ''v: %'', _val;
   END LOOP;
END
'

It looks like this:

test=# \set do 'DECLARE\n   _val  text;\n   _vals text[] := string_to_array(' :'values' ', '','');\nBEGIN\n   FOREACH _val IN ARRAY _vals\n   LOOP\n     RAISE NOTICE ''v: %'', _val;\n   END LOOP;\nEND'
test=# DO :'do';
NOTICE:  v: foo
NOTICE:  v: bar
NOTICE:  v: baz
DO

I added bold emphasis to the variable to make it easier to spot.

Related answer by @Pavel (ab)using a server session variable:

Alternative solutions

Prepared statement

Your current solution doesn't look that bad. I would simplify:

PREPARE get_values AS SELECT * FROM regexp_split_to_table(:'values', ',');

DO
$do$
DECLARE
   _val text;
BEGIN
   FOR _val IN EXECUTE
      'EXECUTE get_values'
   LOOP
      RAISE NOTICE 'v: %', _val;
   END LOOP;
END
$do$;

Temporary table

Similar solution with a temporary table:

CREATE TEMP TABLE tmp AS SELECT * FROM regexp_split_to_table(:'values', ',') v;

DO
$do$
DECLARE
   _val text;
BEGIN
   FOR _val IN
      TABLE tmp
   LOOP
      RAISE NOTICE 'v: %', _val;
   END LOOP;
END
$do$;
Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • Erwin, thank you so much for your answer! Would you mind taking a look at [a solution](http://stackoverflow.com/a/29000817/4660395) I had as well to comment on it's validity? It works, I'm just worried if it's proper/safe. – Alexander Martin Mar 12 '15 at 02:18
  • the do statement can be pretty formated with the help of `select format('multiline statement',[args]) as do \gset` – yaugenka Jun 13 '22 at 13:36
3

Was able to take advantage of this solution:

Where I set the variable as such and retrieve it with current_setting()

\set values foo,bar,baz
SET vars.values TO :'values';

DO $$
DECLARE
    value  TEXT;
    values TEXT[] := string_to_array(current_setting('vars.values'), ',');
BEGIN
    FOREACH value IN ARRAY values LOOP
        RAISE NOTICE 'v: %', value;
    END LOOP;
END $$ LANGUAGE plpgsql
Community
  • 1
  • 1
  • That works, too. Just like the other answer from Pavel. I wouldn't use a [reserved word](http://www.postgresql.org/docs/current/interactive/sql-keywords-appendix.html) like `values` as variable names, even if that one's allowed in Postgres. – Erwin Brandstetter Mar 12 '15 at 02:58
  • @ErwinBrandstetter thank you! They're named differently in the code; I changed the names to value/values for this post. – Alexander Martin Mar 12 '15 at 03:03