I'd like to define a function in PostgreSQL 9.1 that takes multiple INOUT parameters of composite types, but I don't know how to call it.
Eg.
CREATE TYPE my_type_a AS (a integer, b float);
CREATE TYPE my_type_b AS (c boolean, d varchar(5));
CREATE FUNCTION my_complex_func(INOUT a my_type_a, INOUT b my_type_b)
RETURNS RECORD
'...'
LANGUAGE plpgsql;
The definition statements execute just fine, but I don't know how to call this function! I tried:
SELECT INTO a, b
a, b FROM my_complex_func(a, b);
but this gives an error:
ERROR: record or row variable cannot be part of multiple-item INTO list
I don't think it has anything to do with your input types, or the number of them.
Don't return a RECORD, return a real composite type (defined with CREATE TYPE).
The error record or row variable cannot be part of multiple-item INTO list is because you're trying to nest a ROW inside another ROW.
This should work:
CREATE TYPE my_type_a AS (a integer, b float);
CREATE TYPE my_type_b AS (c boolean, d varchar(5));
CREATE TYPE ret_type AS (w integer, v boolean);
CREATE FUNCTION my_complex_func(INOUT a my_type_a, INOUT b my_type_b)
RETURNS ret_type as $$
...
$$ LANGUAGE plpgsql;
And then you can do:
SELECT INTO a, b
(x.comp).w, (x.comp).v
FROM (select my_complex_func(j, i) as comp) x;
This concrete example works for me:
create type smelly1 as (a integer, b text);
create type smelly2 as (a boolean, b float);
create type rettype as (w integer, v boolean);
create function foo_func(n smelly1, m smelly2) returns rettype as $$
declare
f_ret rettype;
begin
f_ret.w := n.a;
f_ret.v := m.a;
return f_ret;
end;
$$ language plpgsql;
select (x.comp).w, (x.comp).v from
(select foo_func('(4, hello)'::smelly1, '(true,3.14)'::smelly2) as comp) x;
returns:
w | v
---+---
4 | t
(1 row)
Related
I have written a SQL function in PostgreSQL that accesses data from another table. On running the function, I am getting following error
relation table2 does not exist postgres
Here is the function that I am creating
CREATE OR REPLACE FUNCTION func(tbl1 table1)
RETURNS TABLE(a int, b text, c int, d text) AS $$
SELECT a, b, c, d
FROM table2
WHERE id = tbl1.user_id;
$$
language sql stable;
Working in case I change table2 to myschema.table2
What do I do? I do not want to add schema into the query. I want it to take whatever schema the function is in.
imitate 38.5.3. SQL Functions on Composite Types (https://www.postgresql.org/docs/current/xfunc-sql.html#XFUNC-SQL-FUNCTION-ARGUMENTS)
db fiddle
CREATE OR REPLACE FUNCTION func(table1)
RETURNS TABLE(a int, b text, c int, d text) AS $$
SELECT a, b, c, d FROM table2 WHERE id = $1.user_id;
$$
language sql;
call it. select (func(table1.*)).* from table1;
I have a postgres DB and I want to create a function that returns a copy of my table with a new column that has a value of 1 if its id is inside the array(idds[]) that the function gets as an input.
In the code below I've try to create a temporary table(chosen) that have id if it's in the idds array and to manually add the isChosen column that obviously doesn't work...
CREATE OR REPLACE FUNCTION public.getTableFromArray(idds integer[])
RETURNS table(
id INTEGER,
isChosen INTEGER
)
LANGUAGE 'plpgsql'
AS $BODY$
begin
with chosen AS(SELECT id,isChosen=1 FROM table1 WHERE ARRAY[table1.id] <# idds)
return query
SELECT id FROM table1 LEFT JOIN chosen ON table1.id=chosen.id;
end;
$BODY$;
Or, with a lot less noise, a proper boolean output column, and without the unhelpful CaMeL case identifiers in a plain SQL function:
CREATE OR REPLACE FUNCTION public.get_table_from_array(idds integer[])
RETURNS TABLE(id int, is_chosen bool)
LANGUAGE sql AS
'SELECT t.id, t.id = ANY(idds) FROM table1 t';
Might as well just run the SQL command directly, though:
SELECT id, id = ANY('{1,2,3}'::int[]) AS is_chosen FROM table1;
you can use this query instead :
select * , case when ARRAY[table1.id] <# idds then 1 else 0 end as choosen FROM table1;
so:
CREATE OR REPLACE FUNCTION public.getTableFromArray(idds integer[])
RETURNS table(
id INTEGER,
isChosen INTEGER
)
LANGUAGE 'plpgsql'
AS $BODY$
begin
return query
select id , case when ARRAY[table1.id] <# idds then 1 else 0 end as isChosen FROM table1;
end;
$BODY$;
I am trying to return a row constructor from a PL/pgSQL function but it is failing with the following error: Returned type record does not match expected type integer in column 1
Here is a simplified version with a plain SQL function as a comparison. The SQL function runs fine, the PL/pgSQL function throws the following error. What am I missing here?
Test Functions
CREATE FUNCTION test_sql()
RETURNS TABLE (
a int,
b int
)
LANGUAGE SQL
IMMUTABLE
AS $$
SELECT (1, 1);
$$;
CREATE FUNCTION test_plpgsql()
RETURNS TABLE (
a int,
b int
)
LANGUAGE plpgsql
IMMUTABLE
AS $$
BEGIN
RETURN QUERY SELECT (1, 1);
END;
$$;
SELECT * FROM test_sql(); -- OK
SELECT * FROM test_plpgsql(); --error
Error Message
[42804] ERROR: structure of query does not match function result type
Detail: Returned type record does not match expected type integer in column 1.
Where: PL/pgSQL function test_plpgsql() line 3 at RETURN QUERY
Use a regular query (returning columns, not a tuple) in RETURN QUERY:
CREATE OR REPLACE FUNCTION test_plpgsql()
RETURNS TABLE (
a int,
b int
)
LANGUAGE plpgsql
IMMUTABLE
AS $$
BEGIN
RETURN QUERY SELECT 1, 1;
END;
$$;
Actually both functions are incorrect. SELECT (1, 1) selects a row of one column of the type record, not two columns of the types integer.
For some reason this can be implicitly casted in the SQL function. But changing it to SELECT 1, 1 in both functions will give you what you want for both.
I see two issues in your design:
it is SRF (Set Returning Function), and it is useless in this case.
You are using SELECT for constant, it is useless too - and it is slower:
postgres=# CREATE OR REPLACE FUNCTION test_plpgsql(OUT a int, OUT b int)
AS $$
BEGIN
a := 1; b := 1;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
CREATE FUNCTION
postgres=# SELECT * FROM test_plpgsql();
┌───┬───┐
│ a │ b │
╞═══╪═══╡
│ 1 │ 1 │
└───┴───┘
(1 row)
What's the difference between a function that returns TABLE vs SETOF records, all else equal.
CREATE FUNCTION events_by_type_1(text) RETURNS TABLE(id bigint, name text) AS $$
SELECT id, name FROM events WHERE type = $1;
$$ LANGUAGE SQL STABLE;
CREATE FUNCTION events_by_type_2(text) RETURNS SETOF record AS $$
SELECT id, name FROM events WHERE type = $1;
$$ LANGUAGE SQL STABLE;
These functions seem to return the same results. See this SQLFiddle.
When returning SETOF record the output columns are not typed and not named. Thus this form can't be used directly in a FROM clause as if it was a subquery or a table.
That is, when issuing:
SELECT * from events_by_type_2('social');
we get this error:
ERROR: a column definition list is required for functions returning
"record"
It can be "casted" into the correct column types by the SQL caller though. This form does work:
SELECT * from events_by_type_2('social') as (id bigint, name text);
and results in:
id | name
----+----------------
1 | Dance Party
2 | Happy Hour
...
For this reason SETOF record is considered less practical. It should be used only when the column types of the results are not known in advance.
This answer is only to remember alternative context where TABLE and SETOF are equivalent.
As #a_horse_with_no_name pointed, it is not a RETURNS SETOF "unknown record", is a defined one.
In this example, the types table and setof are equivalent,
CREATE TYPE footype AS (score int, term text);
CREATE FUNCTION foo() RETURNS SETOF footype AS $$
SELECT * FROM ( VALUES (1,'hello!'), (2,'Bye') ) t;
$$ language SQL immutable;
CREATE FUNCTION foo_tab() RETURNS TABLE (score int, term text) AS $$
SELECT * FROM ( VALUES (1,'hello!'), (2,'Bye') ) t;
$$ language SQL immutable;
SELECT * FROM foo(); -- works fine!
SELECT * FROM foo_tab(); -- works fine and is equivalent.
The RETURNS SETOF have the advantage of reuse type (see footype), that is impossible with RETURNS TABLE.
I have this in a stored proc in SQL Server:
if not exists
(my select statement)
insert T
(a, b, c)
values
(v_a, v_b, v_c) -- arguments passed to the function
select a,b,c from T where...; -- return the row to the client after inserting it
Does postgreSQL have a counterpart to that if exists ( {select-statement} ) construct? The plpgsql compiler at first told me there was a missing "THEN" but when I corrected the if ... then syntax:
create function foo(v_a int, v_b int, v_c int)
returns TABLE (a int, b int, c int)
as $body$
begin
if not exists
(select id from T where ...) then
insert into T
(a, b, c)
values
(v_a, v_b, v_c);
return QUERY
select a,b,c from T where ... ;
end
$body$
LANGUAGE 'plpgsql'
the compilation terminates with:
ERROR: syntax error at end of input
LINE 52: $body$ LANGUAGE 'plpgsql'
.........^
so I'm assuming there's some error upstream, but I don't see it.
Your IF clause is incomplete. It must be closed by the corresponding END IF statement.
Check the docs on conditionals.