Unable to get value for input parameter inside postgres function - sql

In the below postgres function I am passing 'sample' (integer) as an input parma but when I try to print it's value using raise notice inside the function body I get the following error
ERROR: column "sample" does not exist LINE 1: SELECT sample
CREATE OR REPLACE FUNCTION public.test_function(
sample integer)
RETURNS json
LANGUAGE 'sql'
COST 100
VOLATILE PARALLEL UNSAFE
AS $BODY$
DO $$
BEGIN
raise notice 'test: %',sample;
END
$$;
select json_agg(1);
$BODY$;
select "test_function"(10);

It's the anonymous pl/pgsql DO block that does not 'see' the sample parameter. Here is a rewrite in pl/pgsql that does 'see' it.
CREATE OR REPLACE function test_function(sample integer)
RETURNS json LANGUAGE plpgsql COST 100 VOLATILE PARALLEL UNSAFE AS
$BODY$
BEGIN
raise notice 'test: %',sample;
return json_agg(1 + sample);
END
$BODY$;
pl/pgsql DO blocks are more or less encapsulated and can not return anything either.

Related

Postgresql function return table? with different number of columns each time

I have a table called all_checks(code integer,sql_state text) that have some checks on my db.
My table all_checks is like:
code
sql_state
100
select kind,discs,bands from music where kind='rock'
200
select name,surname,type,period from singers where period>1970
....
...
3500
select type,period from concerts
I need to create a function that runs each sql_state from all_checks according to code.
I wrote a function called run_checks(thecode integer) like this:
CREATE OR REPLACE FUNCTION run_checks(thecode integer)
RETURNS SETOF record AS
$$
declare
rec record;
sql_query character varying;
BEGIN
select distinct sql_state
from all_checks
where code=thecode
into sql_query;
RAISE NOTICE 'executing: %',sql_query;
for rec in EXECUTE sql_query loop
return next rec;
end loop;
RETURN;
END
$$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION run_checks(integer)
but when I run for ex select all_checks(200) it gives this error:
ERROR: set-valued function called in context that cannot accept a set
SQL state: 0A000
Context: PL/pgSQL function run_checks(integer) line 18 at RETURN NEXT
I made some search about it and I think the problem is on SETOF record but since I don't know how many columns returns each sql_state, I am confused of what return type my function should have.

Postgresql, function returns query by calling another function

Postgresql 12. Want a function to return query by calling another function but don't know how to call.
create or replace function getFromA()
returns table(_id bigint, _name varchar) as $$
begin
RETURN QUERY SELECT id, name from groups;
end; $$ language plpgsql;
create or replace function getFromB()
returns table(_id bigint, _name varchar) as $$
begin
return query select getFromA();
end; $$ language plpgsql;
select getFromB();
gets error:
SQL Error [42804]: ERROR: structure of query does not match function result type
Detail: Returned type record does not match expected type bigint in column 1.
Where: PL/pgSQL function getfromb() line 3 at RETURN QUERY
How to fix this?
The problem is in getFromB():
return query select getFromA();
Unlike some other databases, Postgres allows set-returning functions directly in the select clause. This works, but can be tricky: this returns a set, hence not the expected structure.
You would need to select ... from getFromA() instead: this way it returns the proper data structure.
create or replace function getFromB()
returns table(_id bigint, _name varchar) as $$
begin
return query select * from getFromA();
end; $$ language plpgsql;
Demo on DB Fiddle

Postgresql Error - Function return type mismatch

I'm looking for some help with a SQL function I am defining under pgAdmin3 for PostGreSQL.
It is a simple function supposed to calculate a ratio given a certain id but when I try to add the function, I get an error message.
Here is the code for the function:
CREATE OR REPLACE FUNCTION data.func_net_exposure(id_fund_arg text)
RETURNS real AS
$BODY$
DECLARE
net_exposure real;
AUM smallint;
BEGIN
AUM := (SELECT sum(cash_fund_total) from main.main_cash where id_fund = id_fund_arg);
net_exposure := (SELECT ROUND(sum(exposure_eur)/(100*AUM)) from main.main_inventory where id_fund = id_fund_arg);
return net_exposure;
END;
$BODY$
And here is the error message I get when I try to add the function:
An error has occurred:
13:13:54: Error: ERROR: return type mismatch in function declared to
return real DETAIL: Function's final statement must be SELECT or
INSERT/UPDATE/DELETE RETURNING. CONTEXT: SQL function
"func_net_exposure"
Any clue on how to solve this error?
The system thinks the function's language is SQL and therefore expects the last statement to be a SELECT or ... RETURNING. The language should be PL/pgSQL. Add a language specification like:
CREATE OR REPLACE FUNCTION data.func_net_exposure(id_fund_arg text)
RETURNS real AS
$BODY$
...
$BODY$
LANGUAGE PLpgSQL;

Using variables from parent in nested postgresql function?

I'm have a pretty long SQL routine that gets a few parameters and runs through a whole bunch of CASE statements. I have the following structure:
DO $$
DECLARE var INTEGER = "someColumn" FROM TABLE LIMIT 1;
BEGIN;
CREATE OR REPLACE FUNCTION pg_temp.fn(var2 boolean) returns decimal AS
$fn$ SELECT CASE WHEN var2 THEN var::decimal ELSE 0::decimal END $fn$ language sql;
$$
And using var inside of fn does not seem to quite work. As in the column does not exist. Is there a way to make it work, am I maybe thinking too complicated?
Edit: fixed the type that was missing. In the original code there is a type declaration, the declaration is not the problem. Using the variable in the nested function is.
First of all. THIS IS NOT THE BEST PRACTICE. It can be done, but it is not recommended to use nested functions.
The problem is that var is in a different scope than the function. You have to "write" the function as an independent unit. You can do this with EXECUTE and FORMAT.
Here is an example of a function that shows var as a message:
DO $$
DECLARE
sql text; -- To "write" the function
var integer := id FROM table_name ORDER BY id LIMIT 1;
BEGIN
-- To make it hapend I use $test$ to set the function.
-- FORMAT is to allocate var into the function through '%s'.
-- you have to use the correct sintax acording to the data type.
sql := FORMAT($test$
CREATE OR REPLACE FUNCTION test() RETURNS void AS $FUNCTION$
BEGIN
RAISE NOTICE '%s';
END; $FUNCTION$ LANGUAGE plpgsql
$test$,var);
EXECUTE sql;
END; $$

Generic set returning function

I have a plpgsql function which act as a wrapper around several other functions, all of which are set returning functions of different data types. Through this function, I am trying to get a generic set returning function which can return set of any data type. The data type of record is decided based on the input table name and column name.
Here is the code snippet:
create function cs_get(tablename text, colname text) returns setof record as $$
declare
type_name text;
ctype text;
loadfunc text;
result record;
begin
select data_type into type_name from information_schema.columns where table_name = tablename and column_name = colname;
if type_name is null then
raise exception 'Table % doesnot exist or does not have attribute % ',tablename, colname;
end if;
ctype := cs_get_ctype(type_name);
loadfunc := 'select * from cs_get_'||ctype||'('''||tablename||''','''||colname||''')';
for result in execute loadfunc loop
return next result;
end loop;
return;
end; $$ language plpgsql;
Suppose the column is of type integer (corresponding c type is int64), loadfunc would be
select * from cs_get_int64(tablename, colname)
cs_get_int64 is a set-returning function defined in a C library which returns values of type int64.
However, this gives an error saying
ERROR: a column definition list is required for functions returning "record" at character 15
The easiest way to achieve this is to return a setof text for every data type. But that of course is not a clean way to do this. I have tried replacing loadFunc with
select cs_get_int64::integer from cs_get_int64(tablename, colname)
which is required to use records. But, this did not help.
My question now is:
Is it possible to create such a generic set returning function? If yes, how?
The answer is yes. But it's not trivial.
As long as you return anonymous records (returns setof record) a column definition list is required for the returned record to work with it in SQL. Pretty much what the error message says.
There is a (limited) way around this with polymorphic types:
CREATE OR REPLACE FUNCTION cs_get(_tbl_type anyelement, _colname text)
RETURNS SETOF anyelement AS
...
Detailed explanation int this related answer (climax in the last chapter!):
Refactor a PL/pgSQL function to return the output of various SELECT queries