Let’s say I have a function that returns type record
:
CREATE OR REPLACE FUNCTION pnh_row(myId integer)
RETURNS RECORD AS $$
DECLARE
myRow RECORD;
BEGIN
// blah blah
return myRow;
END;
$$ language plpgsql;
How can I insert and update this record into a table (preferably without naming each individual field).
I tried:
insert into mytable select pnh_row(1234);
but I get:
ERROR: column "id" is of type integer but expression is of type record
LINE 1: insert into mytable select pnh_row(1234);
^
HINT: You will need to rewrite or cast the expression.
Ideally, I could also update the whole record from this tuple also, but I can’t think what syntax to even try. I guess I could do either one by tediously listing out every column, but is there a way to avoid that?
2
Answers
If your function returns a
record
, you must write a column definition list at the call site, there is no way to avoid that. So instead give the function the proper return type, the row type of the table in which you want to insert:RETURNS mytable
. Then you can runDisclaimer
Like I commented, the best course of action is not to make that function return an anonymous
record
to begin with. There are typically better options.If you indeed need a function, here are some related answers:
Function returning a table for given table and column names
How to return multiple rows from PL/pgSQL function?
Refactor a PL/pgSQL function to return the output of various SELECT queries
Solution
While stuck with your unfortunate situation, there is a way to make it work without naming each individual field:
Your attempt:
… does not work, because the
INSERT
tries to assign the returned value (the whole anonymousrecord
) to the first columnid
ofmytable
(apparently aninteger
), which raises a type mismatch exception.You cannot decompose the returned
record
since, being anonymous, its structure is unknown:The manual:
You would have to tediously list out every column – each followed by its data type – the thing you want to avoid.
You cannot cast it to the row type
mytable
because there is no registered cast.However, everything can be cast to and from
text
. So you can usetext
as stepping stone:But that would make the bad solution even worse. Decomposing this way results in a separate function call for every nested column.
Instead, move the function call to a subquery to execute it once, and only decompose in the
SELECT
list:Of course, the
record
type has to match the row type ofmy_table
.See:
An
UPDATE
can use a similar trick to avoid spelling out a column definition list: