Postgres 9.1 Type of SETOF record - postgresql

I have dynamicly generated SELECT. I try to return result as SETOF RECORD. Sth like that:
CREATE FUNCTION test(column_name text) RETURNS SETOF RECORD AS $$
DECLARE
row RECORD;
BEGIN
FOR row IN EXECUTE 'SELECT ' || quote_ident(column_name) || ' FROM dates'
LOOP
RETURN NEXT row;
END LOOP;
RETURN;
END;
$$ LANGUAGE 'plpgsql';
When I try:
SELECT * FROM test('column1');
I get this:
ERROR: a column definition list is required for functions returning "record"
I know that column1 is integer type:
SELECT * FROM test('column1') f(a int);
result is correct, because I know that this is going to be Integer type.
When I try:
SELECT * FROM test('column1') f(a varchar);
I get error:
ERROR: wrong record type supplied in RETURN NEXT
DETAIL: Returned type integer does not match expected type character varying in column 1.
Now my question:
What to do to get rid of part of querty where I define types 'f(a int)'. It should by feasible because Postgres knowns what is returned type. I tried with IMMUTABLE options, but unsuccessfully.

You could cast the value to text inside the function, and declare that the function RETURNS SETOF text. You can also return the whole result set at once; no need to iterate explicitly.
CREATE TABLE dates (column1 int, column2 date);
INSERT INTO dates VALUES (1, date '2012-12-22'), (2, date '2013-01-01');
CREATE FUNCTION test(column_name text) RETURNS SETOF text AS $$
BEGIN
RETURN QUERY EXECUTE 'SELECT '
|| quote_ident(column_name) || '::text FROM dates';
END;
$$ LANGUAGE 'plpgsql';
Now SELECT test('column1'); yields:
test
------
1
2
(2 rows)
... and (with my locale settings) SELECT test('column2'); yields:
test
------------
2012-12-22
2013-01-01
(2 rows)

You need to specify OUT parameters corresponding to the columns you want to return.

Related

Declare a Table as a variable in a stored procedure?

I am currently working a stored procedure capable of detecting continuity on a specific set of entries..
The specific set of entries is extracted from a sql query
The function takes in two input parameter, first being the table that should be investigated, and the other being the list of ids which should be evaluated.
For every Id I need to investigate every row provided by the select statement.
DROP FUNCTION IF EXISTS GapAndOverlapDetection(table_name text, entity_ids bigint[]);
create or replace function GapAndOverlapDetection ( table_name text, enteity_ids bigint[] )
returns table ( entity_id bigint, valid tsrange, causes_overlap boolean, causes_gap boolean)
as $$
declare
x bigint;
var_r record;
begin
FOREACH x in array $2
loop
EXECUTE format('select entity_id, valid from' ||table_name|| '
where entity_id = '||x||'
and registration #> now()::timestamp
order by valid ASC') INTO result;
for var_r in result
loop
end loop;
end loop ;
end
$$ language plpgsql;
select * from GapAndOverlapDetection('temp_country_registration', '{1,2,3,4}')
I currently get an error in the for statement saying
ERROR: syntax error at or near "$1"
LINE 12: for var_r in select entity_id, valid from $1
You can iterate over the result of the dynamic query directly:
create or replace function gapandoverlapdetection ( table_name text, entity_ids bigint[])
returns table (entity_id bigint, valid tsrange, causes_overlap boolean, causes_gap boolean)
as $$
declare
var_r record;
begin
for var_r in EXECUTE format('select entity_id, valid
from %I
where entity_id = any($1)
and registration > now()::timestamp
order by valid ASC', table_name)
using entity_ids
loop
... do something with var_r
-- return a row for the result
-- this does not end the function
-- it just appends this row to the result
return query
select entity_id, true, false;
end loop;
end
$$ language plpgsql;
The %I injects an identifier into a string and the $1 inside the dynamic SQL is then populated through passing the argument with the using keyword
Firstly, decide whether you want to pass the table's name or oid. If you want to identify the table by name, then the parameter should be of text type and not regclass.
Secondly, if you want the table name to change between executions then you need to execute the SQL statement dynamically with the EXECUTE statement.

plpgsql - selecting array of multiple columns

I am trying to create procedure which selects data, processes and returns them, but I am struggling how to define array variable for multiple columns.
This works:
CREATE OR REPLACE FUNCTION testing_array_return()
RETURNS TABLE(id BIGINT) AS
$body$
DECLARE
l_rows BIGINT[];
BEGIN
-- select data using for update etc
l_rows := ARRAY(
SELECT 1 AS id
UNION
SELECT 2 AS id
);
-- do some stuff
-- return previously selected data
RETURN QUERY
SELECT *
FROM UNNEST(l_rows);
END;
$body$
LANGUAGE 'plpgsql'
But I want to do this for 2 or more columns without using composite type or rowtype:
CREATE OR REPLACE FUNCTION testing_array_return()
RETURNS TABLE(id BIGINT, text VARCHAR2) AS
$body$
DECLARE
l_rows -- what should I put here?
BEGIN
-- select data using for update etc
l_rows := ARRAY(
SELECT 1 AS id, 'test' AS text
UNION
SELECT 2 AS id, 'test2' AS text
);
-- do some stuff
-- return previously selected data
RETURN QUERY
SELECT *
FROM UNNEST(l_rows);
END;
$body$
LANGUAGE 'plpgsql'
In oracle I could define record type and then table type of it, but I can't find how to do this in postgres. Maybe using wrong keywords when searching...
edit: this is how I would do this in Oracle (without returning).
DECLARE
TYPE t_row IS RECORD(
id NUMBER
,text VARCHAR2(10));
TYPE t_tbl IS TABLE OF t_row;
l_rows t_tbl := t_tbl(); --how to do this in postgres?
BEGIN
SELECT *
BULK COLLECT
INTO l_rows
FROM (SELECT 1 AS id
,'test' AS text
FROM dual
UNION ALL
SELECT 2 AS id
,'test' AS text
FROM dual);
END;
Anything similiar in postgres? Like record but for array.
You would do the same in Postgres:
Create the record type:
create type footype as (id bigint, text_ varchar);
Then use an array of that type in your function:
CREATE OR REPLACE FUNCTION testing_array_return()
RETURNS TABLE(id BIGINT, text VARCHAR) AS
$body$
DECLARE
l_rows footype[];
BEGIN
-- select data using for update etc
l_rows := ARRAY(
SELECT (1, 'test')
UNION
SELECT (2, 'test2')
);
-- do some stuff
-- return previously selected data
RETURN QUERY
SELECT *
FROM UNNEST(l_rows);
END;
$body$
LANGUAGE plpgsql;
Online example: http://rextester.com/UMRZFO54266
The expression (1, 'test') creates a single value of record type. As that is assigned to a typed variable, there is no need to alias the columns (and in fact you can't do that anyway).
Unrelated, but: the language name is an identifier. Do not put that in single quotes.
Note that text is also a keyword in Postgres because it's a data type. You shouldn't use it as a column name

Function returns only one column when return type is record type postgres

I have a function that has the return type of record.
Below is the function am using, basically what the function does is , it takes in input paramter and queries the database to return the columns:
drop function if exists test_proc(sr_number1 bigint);
create or replace function test_proc(sr_number1 bigint) RETURNS record /*SETOF tbl*/ AS $$
declare
i integer default 0;
record_type record;
begin
select sr_num,product_number,phone,addr into record_type from my_table where sr_num=sr_number1;
return record_type;
end
$$ LANGUAGE plpgsql;
Unfortunately, when I execute the function as
select test_proc(12345); I get the result as a comma separated list in just one column like (sr_num,product_number,phone,addr). But what I was hoping to have it return was a table row with the column values and their respective column names.
I also tried executing the function as
select * from test_proc(12345); but get the following error
ERROR: a column definition list is required for functions returning "record"
When querying a function that returns a record you must specify the type of record you want to get in the result
select * from test_proc(12345) f(b bigint, t text);
This should work.
A better solution is to declare the type of record in the function
CREATE OR REPLACE FUNCTION test_proc(sr_number1 bigint)
RETURNS TABLE(b bigint, t text) AS $$ $$

PostgreSQL: How to display only selected columns of a single table within CASE expression in function?

Example: I am passing two parameters to function namely n(case number) and tname(table name), and want to display rows accordingly.
--Table "testing"
create table testing
(
rollno integer,
fname text,
lname text,
age integer,
branch text,
phno integer,
email text,
address text,
city text,
state text,
country text
)
--Rows insertion
insert into testing values(1,'aaa','bbb',25,'CS',1234567890,'abc#gmail.com','sector1','xyz','zyx','yxz');
insert into testing values(2,'zzz','xxx',25,'EE',987654321,'zzz#gmail.com','sector2','uvw','wvu','vuw');
--Function "f1()"
create or replace function f1(n integer,tname varchar)/*n for case number and tname for table name */
returns setof tname as
$body$
begin
case n
when 1 then
return query execute format ($$ select rollno,fname from %I $$,tname);
when 2 then
return query execute format ($$ select lname,age,branch from %I $$,tname);
when 3 then
return query execute format ($$ select phno,email,address,city,country from %I $$,tname);
end case;
end
$body$
language plpgsql;
--Function calling
select * from f1(1,'testing');
/*Show only case "1" select query result*/
select * from f1(2,'testing');
/*Show only case "2" select query result*/
select * from f1(3,'testing');
/*Show only case "3" select query result*/
While Craig is correct that return types cannot be dynamic in function declarations, there is a way around this with polymorphic types. This is surprisingly simple and would actually work flawlessly:
CREATE OR REPLACE FUNCTION data_of(_tbl_type anyelement)
RETURNS SETOF anyelement AS
$func$
BEGIN
RETURN QUERY EXECUTE 'SELECT * FROM '|| pg_typeof(_tbl_type);
END
$func$ LANGUAGE plpgsql;
Call (important!):
SELECT rollno,fname FROM data_of(NULL::testing);
SELECT * FROM data_of(NULL::my_schema.my_table);
SELECT * FROM data_of(NULL::my_custom_type);
What you need is a well-known type. For every table there is a well-known type automatically. But you can create any type, cast NULL to it and pass it to the function. This way you can build exactly what you have in your question ...
Related answer with a lot more details:
Refactor a PL/pgSQL function to return the output of various SELECT queries

How to return multiple rows from PL/pgSQL function?

I have spent good amount of time trying to figure it out and I haven't been able to resolve it. So, I need your help please.
I am trying to write a PL/pgSQL function that returns multiple rows. The function I wrote is shown below. But it is not working.
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS SETOF RECORD
AS
$$
DECLARE result_record keyMetrics;
BEGIN
return QUERY SELECT department_id into result_record.visits
from fact_department_daily
where report_date='2013-06-07';
--return result_record;
END
$$ LANGUAGE plpgsql;
SELECT * FROM get_object_fields;
It is returning this error:
ERROR: RETURN cannot have a parameter in function returning set;
use RETURN NEXT at or near "QUERY"
After fixing the bugs #Pavel pointed out, also define your return type properly, or you have to provide a column definition list with every call.
This call:
SELECT * FROM get_object_fields()
... assumes that Postgres knows how to expand *. Since you are returning anonymous records, you get an exception:
ERROR: a column definition list is required for functions returning "record"
One way (of several) to fix this is with RETURNS TABLE (Postgres 8.4+):
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS TABLE (department_id int) AS
$func$
BEGIN
RETURN QUERY
SELECT department_id
FROM fact_department_daily
WHERE report_date = '2013-06-07';
END
$func$ LANGUAGE plpgsql;
Works for SQL functions just the same.
Related:
PostgreSQL: ERROR: 42601: a column definition list is required for functions returning "record"
I see more bugs:
first, a SET RETURNING FUNCTIONS call has following syntax
SELECT * FROM get_object_fields()
second - RETURN QUERY forwards query result to output directly. You cannot store this result to variable - it is not possible ever in PostgreSQL now.
BEGIN
RETURN QUERY SELECT ....; -- result is forwarded to output directly
RETURN; -- there will not be any next result, finish execution
END;
third - these simple functions is better to implement in SQL languages
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS SETOF RECORD AS $$
SELECT department_id WHERE ...
$$ LANGUAGE sql STABLE;
Here's one way
drop function if exists get_test_type();
drop type if exists test_comp;
drop type if exists test_type;
drop type if exists test_person;
create type test_type as (
foo int,
bar int
);
create type test_person as (
first_name text,
last_name text
);
create type test_comp as
(
prop_a test_type[],
prop_b test_person[]
);
create or replace function get_test_type()
returns test_comp
as $$
declare
a test_type[];
b test_person[];
x test_comp;
begin
a := array(
select row (m.message_id, m.message_id)
from message m
);
-- alternative 'strongly typed'
b := array[
row('Bob', 'Jones')::test_person,
row('Mike', 'Reid')::test_person
]::test_person[];
-- alternative 'loosely typed'
b := array[
row('Bob', 'Jones'),
row('Mike', 'Reid')
];
-- using a select
b := array (
select row ('Jake', 'Scott')
union all
select row ('Suraksha', 'Setty')
);
x := row(a, b);
return x;
end;
$$
language 'plpgsql' stable;
select * from get_test_type();
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS table (department_id integer)
AS
$$
DECLARE result_record keyMetrics;
BEGIN
return QUERY
SELECT department_id
from fact_department_daily
where report_date='2013-06-07';
--return result_record;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM get_object_fields()