Set concatenated string as a application_name - postgresql

I'm trying to set application_name with some concatenated string as shown below in the example:
Example:
do
$$
declare var1 text := 'Text1';
var2 text := 'Text2';
result text;
begin
set application_name = var1||'-'||var2;
select application_name into result
from pg_stat_activity where pid = pg_backend_pid();
raise info '%',result;
end;
$$;
But getting an error:
SQL Error [42601]: ERROR: syntax error at or near "||" Position: 116

Use format to concatenate the string and then execute it, e.g.
do
$$
declare var1 text := 'Text1';
var2 text := 'Text2';
result text;
begin
execute format('set application_name=%s',quote_ident(var1||'-'||var2));
select application_name into result
from pg_stat_activity where pid = pg_backend_pid();
raise info '%',result;
end;
$$;

Related

Variables in Bash postgres loop [duplicate]

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;
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.
How to concatenate psql variables?
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:
Referring to session variables (\set var='value') from PL/PGSQL
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$;
Was able to take advantage of this solution:
Passing argument to a psql procedural script
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

How to query last_value of a sequence using late-binding

Is it possible to obtain the last value from a sequence of which name is stored in a variable?
seq_name := 'myschema.mysequence';
SELECT last_value FROM ${seq_name};
If the sequence has already been used in the session, you can simply user the currval function:
DO
$$DECLARE
var text := 'seq';
BEGIN
RAISE NOTICE 'The current value is %', currval(var);
END;$$;
NOTICE: The next value is 1
Otherwise, you need dynamic SQL:
DO
$$DECLARE
var text := 'seq';
value bigint;
BEGIN
EXECUTE format('SELECT last_value FROM %s', var) INTO value;
RAISE NOTICE 'The current value is %', value;
END;$$;
NOTICE: The current value is 1

Use variable set by psql meta-command inside of DO block

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;
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.
How to concatenate psql variables?
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:
Referring to session variables (\set var='value') from PL/PGSQL
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$;
Was able to take advantage of this solution:
Passing argument to a psql procedural script
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

PostgreSQL: Select string_agg into variable

I have a string to execute with the string aggregate function within the postgresql function. Here is the following script for that.
Example:
create or replace function f(colvalue int,colnvalue varchar)
returns void as
$$
declare
sql varchar;
var varchar;
begin
sql := 'Select var:= string_agg(................) /* Error occurred here near var:= */
from tablename where cola ='|| colvalue || ' AND coln ='|| colnvalue;
raise notice '%'sql;
execute sql into var;
raise notice var;
end;
$$
language plpgsql;
Error:
ERROR: syntax error at or near ":="
Note: I want the result of string_agg into var.
Dynamic SQL can contains SQL statement only - but ":=" is a PL/pgSQL statement. Next, it is clean from your example, so it is useless there. Second issue is a SQL injection vulnerability (still this code should not work). Newer use a patter ' || varcharvar || ' for SQL used in dynamic SQL.
CREATE OR REPLACE FUNCTION f(colvalue int,colnvalue varchar)
RETURNS void AS $$
DECLARE
sql varchar;
var varchar;
BEGIN
sql := 'SELECT string_agg(..) FROM tablename WHERE cola=$1 AND coln=$2';
RAISE NOTICE '%', sql;
EXECUTE sql INTO var USING colvalue, colnvalue;
RAISE NOTICE '%', var;
END;
$$ LANGUAGE plpgsql;

PostgreSQL: How to assign "SELECT" query to variable using dblink?

Here I am trying to assign a "SELECT" query to a specific variable. The select query is retrieving rows from other database table using dblink. For example as shown below:
Example: My try
create or replace function fun(names varchar(60), id bigint) returns void as
$$
Declare
sql varchar;
Begin
sql := 'Select * from dblink'('conn','select * from tablex') /* Error occurred here */
'where name = ''' || names ||'''';
raise info '%',sql;
execute sql,'names varchar(60)',names=names;
end;
$$
Language plpgsql;
ERROR: syntax error at or near "("
You've messed up your quotimg.
You can use dollar-quoting inside the fumction too.
sql := $SQL$...$SQL$
Solution: Here is the solution
create or replace function fun(names varchar(60), id bigint) returns void as
$$
Declare
sql varchar;
Begin
sql := 'Select * from dblink(''conn','select * from tablex'')
where name = ''' || names ||'''';
raise info '%',sql;
execute sql,'names varchar(60)',names=names;
end;
$$
Language plpgsql;