Postgres function: assiging results from built-in function to variable - sql

How to assign results of REPLACE() built-in Postgres function to another variable?
I am trying something like this, but it does not work:
CREATE FUNCTION uri2text(uri text) RETURNS text AS $$
SET temp_text = SELECT REPLACE(uri , '%20', ' ');
RETURN temp_text;
$$ LANGUAGE SQL;

You can't have variables in SQL (which is the language that language sql selects for the function). To use variables, you need PL/pgSQL, and as documented in the manual assignment is done using := (or =) in PL/pgSQL. And you need to declare a variable before you can use it.
You also don't need a SELECT statement to call a function.
So if you do want to use PL/pgSQL, the function needs to look like this:
CREATE FUNCTION uri2text(uri text)
RETURNS text
AS $$
declare
temp_text text;
begin
temp_text := REPLACE(uri , '%20', ' ');
RETURN temp_text;
end;
$$ LANGUAGE plpgsql;
However you wouldn't even need a variable:
CREATE FUNCTION uri2text(uri text)
RETURNS text
AS $$
begin
RETURN REPLACE(uri , '%20', ' ');
end;
$$ LANGUAGE plpgsql;
And you don't even need PL/pgSQL for this:
CREATE FUNCTION uri2text(uri text)
RETURNS text
AS $$
SELECT REPLACE(uri , '%20', ' ');
$$ LANGUAGE sql;
Note that in case of the SQL function, you do need the SELECT because in SQL there is no return or assignment.

Related

How do I return a table from a function with a bespoke column name?

This function works:
CREATE OR REPLACE FUNCTION public.a()
RETURNS TABLE(a text)
LANGUAGE plpgsql
AS $function$
BEGIN
return query execute
'select a from ztable';
END;
$function$;
But when I try to add some text to the column name:
CREATE OR REPLACE FUNCTION public.a(prefix text)
RETURNS TABLE(a text)
LANGUAGE plpgsql
AS $function$
BEGIN
return query execute
'select a as $1_a from ztable' using prefix;
END;
$function$;
This just fails as a syntax error on $1.
Or:
CREATE OR REPLACE FUNCTION public.a(prefix text)
RETURNS TABLE(a text)
LANGUAGE plpgsql
AS $function$
BEGIN
return query execute
'select a as '||prefix||'_a from ztable';
END;
$function$;
select * from a('some prefix') doesn't work.
Is there some other syntax that does the job?
That's simply not possible. SQL does not allow dynamic column names.
You must assign a column alias with the call. Like:
SELECT a AS prefix_a FROM public.a();
Or in a column definition list directly attached to the function:
SELECT * FROM public.a() AS f(prefix_a);
Or, while dealing with a single output column, even just:
SELECT * FROM public.a() AS prefix_a;
See:
RETURNING rows using unnest()?

How to return the value of function 2 from within function 1

How would I return the return value of a function from within a function (eg. foo() calls
bar())?
create or replace function bar ()
returns text
as $$
begin
select md5(random()::text);
end
$$ language 'plpgsql';
create or replace function foo ()
returns text
as $$
begin
return select bar ();
end
$$ language 'plpgsql';
I keep getting errors like query has no destination for result data
You need to provide something to accept the output of the query:
CREATE OR REPLACE FUNCTION public.bar()
RETURNS text
LANGUAGE plpgsql
AS $function$
declare
md5_val text;
begin
select into md5_val md5(random()::text);
return md5_val;
end
$function$
;
create or replace function foo ()
returns text
as $$
declare
md5_val2 text;
begin
select into md5_val2 bar();
return md5_val2;
end
$$ language 'plpgsql';
select * from foo();
foo
----------------------------------
ac6a4910fac3472d226dc54bb147336e
See:
https://www.postgresql.org/docs/current/plpgsql-statements.html#PLPGSQL-STATEMENTS-SQL-ONEROW
Both of your functions are some impermissible mixture of 'plpgsql' language and 'sql' language functions. Also, it isn't about calling one from the other, bar() is broken even if you just call it directly, and once fixed then foo() is broken independently of bar() being broken.
Nothing here needs to be in plpgsql, so the simplest repair is just to turn them into 'sql' language functions.
create or replace function bar ()
returns text
as $$
select md5(random()::text);
$$ language 'sql';
create or replace function foo ()
returns text
as $$
select bar();
$$ language 'sql';
If you want them to be plpgsql, then in first case you need to return something, it doesn't just automatically return the result of the last query like SQL language functions do. And in the second one, "return select" is not valid. You could select into a scratch variable and then return that scratch variable, but it easier just to change the "select" to "return" in the first, and remove the wayward "select" in the second.
create or replace function bar ()
returns text
as $$
begin
return md5(random()::text);
end
$$ language 'plpgsql';
create or replace function foo ()
returns text
as $$
begin
return bar();
end
$$ language 'plpgsql';

Postgres function to return Table into variables

How can I capture different columns into different variables like so (note this is only pseudocode so I am assuming it will cause errors. Example taken from here)
create or replace function get_film (
p_pattern varchar
)
returns table (
film_title varchar,
film_release_year int
)
language plpgsql
as $$
begin
return query
select
title,
release_year::integer
from
film
where
title ilike p_pattern;
end;$$
create or replace function get_film_into_variables (
p_pattern varchar
)
returns null
language plpgsql
as $$
declare
v_title varchar,
v_release_year integer
begin
SELECT
get_film (p_pattern)
INTO
v_title,
v_release_year;
end;$$
Assuming you have some purpose for the variables after retrieving them not just ending the function your "get_film_into_variables" is almost there. But first let's backup just a bit. A function that returns a table does just that, you can use the results just like a table stored on disk (it just goes away after query or calling function ends). To that end only a slight change to the "get_film_into_variables" function is required. The "get_film" becomes the object of the FROM clause. Also change the returns null, to returns void. So
create or replace function get_film_into_variables (
p_pattern varchar
)
returns void
language plpgsql
as $$
declare
v_title varchar;
v_release_year integer;
begin
select *
from get_film (p_pattern)
INTO
v_title,
v_release_year;
end;
$$;
The above works for a single row returned by a function returning table. However for a return of multiple rows you process the results of the table returning function just lake you would an actual table - with a cursor.
create or replace
function get_film_into_variables2(p_pattern varchar)
returns void
language plpgsql
as $$
declare
k_message_template constant text = 'The film "%s" was released in %s.';
v_title varchar;
v_release_year integer;
v_film_message varchar;
c_film cursor (c_pattern varchar) for
select * from get_film (c_pattern);
begin
open c_film (p_pattern);
loop
fetch c_film
into v_title
, v_release_year;
exit when not found;
v_film_message = format( k_message_template,v_title,v_release_year::text);
raise notice using
message = v_film_message;
end loop;
end;
$$;
BTW: the get_film function can be turned into a SQL function. See fiddle here. For demo purposes get_film_into_variable routines return a message.

Create function in dynamic sql PostgreSQL

Is it possible to create a function or execute anonymous block inside dynamic SQL in PostgreSQL?
I'm looking for something like this:
Create or replace FUNCTION fff(p1 int)
LANGUAGE plpgsql
AS $$
DECLARE
v_Qry VARCHAR(4000);
BEGIN
v_Qry := '
Create or replace FUNCTION fff_DYNAMIC_SQL()
LANGUAGE plpgsql
AS $$
DECLARE
v1 INTEGER;
begin
v1 := ' || p1 || ';
RETURN;
END; $$;';
EXECUTE v_Qry;
RETURN;
END; $$;
You have three levels of nested string in your code. The best way to deal with that, is to use dollar quoting for all of them. When creating dynamic SQL it's also better to use format() instead of string concatenation. Then you only need a single string with placeholders which makes the code a lot easier to read.
To nest multiple dollar quoted strings use a different delimiter each time:
Create or replace FUNCTION fff(p1 int)
returns void
LANGUAGE plpgsql
AS
$$ --<< outer level quote
DECLARE
v_Qry VARCHAR(4000);
BEGIN
v_Qry := format(
$string$ --<< quote for the string constant passed to the format function
Create or replace FUNCTION fff_DYNAMIC_SQL()
returns void
LANGUAGE plpgsql
AS
$f1$ --<< quoting inside the actual function body
DECLARE
v1 INTEGER;
begin
v1 := %s;
RETURN;
END;
$f1$
$string$, p1);
EXECUTE v_Qry;
RETURN;
END;
$$;
You also forgot to declare the returned data type. If the function does not return anything, you need to use returns void.

Return set of records with unknown table_name

I want to return a row from a table with this function (I don't know the name of the table, it's random)
CREATE FUNCTION foo( text ) RETURNS setof record AS $$
DECLARE
table_name ALIAS FOR $1;
BEGIN
SELECT * from table_name ;
END
$$ LANGUAGE plpgsql;
and then I want to do this:
select col1,col2 from foo('bar');
Any ideas?
SQL demands to know the return type at call time. And functions require you to define a return type as well. What you are after is not trivial.
If you don't know the return type at call time, you are basically out of luck. You cannot solve the problem with a single function call.
If you know the type at call time, there is an option with polymorphic types and dynamic SQL with EXECUTE:
CREATE OR REPLACE FUNCTION f_data_of_table(_tbl_type anyelement)
RETURNS SETOF anyelement AS
$func$
BEGIN
RETURN QUERY EXECUTE
'SELECT * FROM ' || pg_typeof(_tbl_type);
END
$func$ LANGUAGE plpgsql;
Call:
SELECT * FROM f_data_of_table(NULL::my_table_name);
Details in this related answer:
Refactor a PL/pgSQL function to return the output of various SELECT queries
Be wary of SQL injection:
Table name as a PostgreSQL function parameter
Only makes sense if you do more than just SELECT * FROM tbl, or you'd simply use the SQL command.
Aside:
Do not use ALIAS to attach names to parameter values. That's outdated and discouraged. Use named parameters instead.