Thread: a column definition list is required for functions returning "record"
Good afternon,
in 9.5.3 I have defined the following custom function:
CREATE OR REPLACE FUNCTION words_select_games(IN in_uid integer)
RETURNS SETOF RECORD AS
$func$
BEGIN
RETURN QUERY SELECT
g.gid AS gid,
EXTRACT(EPOCH FROM g.created)::int AS created,
g.player1 AS player1,
COALESCE(g.player2, 0) AS player2,
COALESCE(EXTRACT(EPOCH FROM g.played1)::int, 0) AS played1,
COALESCE(EXTRACT(EPOCH FROM g.played2)::int, 0) AS played2,
ARRAY_TO_STRING(g.hand1, '') AS hand1,
REGEXP_REPLACE(ARRAY_TO_STRING(g.hand2, ''), '.', '?', 'g') AS hand2,
g.letters AS letters, /* is a varchar[15][15] */
g.values AS values, /* is an integer[15][15] */
g.bid AS bid,
m.tiles AS last_tiles,
m.score AS last_score
FROM words_games g LEFT JOIN words_moves m USING(mid)
WHERE g.player1 = in_uid
UNION SELECT
g.gid AS gid,
EXTRACT(EPOCH FROM g.created)::int AS created,
g.player2 AS player1,
COALESCE(g.player2, 0) AS player1,
COALESCE(EXTRACT(EPOCH FROM g.played2)::int, 0) AS played1,
COALESCE(EXTRACT(EPOCH FROM g.played1)::int, 0) AS played2,
ARRAY_TO_STRING(g.hand2, '') AS hand1,
REGEXP_REPLACE(ARRAY_TO_STRING(g.hand1, ''), '.', '?', 'g') AS hand2,
g.letters AS letters,
g.values AS values,
g.bid AS bid,
m.tiles AS last_tiles,
m.score AS last_score
FROM words_games g LEFT JOIN words_moves m USING(mid)
WHERE g.player2 = in_uid;
END
$func$ LANGUAGE plpgsql;
but calling it gives me errors:
words=> select * from words_select_games(1);ERROR: a column definition list is required for functions returning "record"LINE 1: select * from words_select_games(1);^words=> select gid, bid from words_select_games(1); ERROR: a column definition list is required for functions returning "record"LINE 1: select gid, bid from words_select_games(1);^
I have also unsuccessfully tried
RETURNS SETOF words_games, words_moves AS
and without the comma:
RETURNS SETOF words_games words_moves AS
How would you recommend to fix my declaration problem please?
Regards
Regards
Alex
Alexander Farber <alexander.farber@gmail.com> writes: > in 9.5.3 I have defined the following custom function: > CREATE OR REPLACE FUNCTION words_select_games(IN in_uid integer) > RETURNS SETOF RECORD AS > I have also unsuccessfully tried > RETURNS SETOF words_games, words_moves AS > How would you recommend to fix my declaration problem please? I think you are looking for the RETURNS TABLE syntax. regards, tom lane
Re: a column definition list is required for functions returning "record"
From
Alexander Farber
Date:
Thank you, I was just wondering if there is a simpler way... but ok
On Fri, Aug 26, 2016 at 5:29 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I think you are looking for the RETURNS TABLE syntax.
Hi
From the documentation... ( https://www.postgresql.org/ docs/current/static/sql- select.html )
'Function calls can appear in the FROM clause. (This is especially useful for functions that return result sets, but any function can be used.) This acts as though the function's output were created as a temporary table for the duration of this single SELECT command...
If the function has been defined as returning the record data type, then an alias or the key word AS must be present, followed by a column definition list in the form ( column_name data_type [, ... ]). The column definition list must match the actual number and types of columns returned by the function.'
You need to use 'returns table' syntax or to add an alias in your query.From the documentation... ( https://www.postgresql.org/
'Function calls can appear in the FROM clause. (This is especially useful for functions that return result sets, but any function can be used.) This acts as though the function's output were created as a temporary table for the duration of this single SELECT command...
If the function has been defined as returning the record data type, then an alias or the key word AS must be present, followed by a column definition list in the form ( column_name data_type [, ... ]). The column definition list must match the actual number and types of columns returned by the function.'
select * from words_select_games(1) as (gid type, created type, player1 type, ...);
Check for the correct column types
Regards,
WalterOn Fri, Aug 26, 2016 at 11:20 AM, Alexander Farber <alexander.farber@gmail.com> wrote:
Good afternon,in 9.5.3 I have defined the following custom function:CREATE OR REPLACE FUNCTION words_select_games(IN in_uid integer)RETURNS SETOF RECORD AS$func$BEGINRETURN QUERY SELECTg.gid AS gid,EXTRACT(EPOCH FROM g.created)::int AS created,g.player1 AS player1,COALESCE(g.player2, 0) AS player2,COALESCE(EXTRACT(EPOCH FROM g.played1)::int, 0) AS played1,COALESCE(EXTRACT(EPOCH FROM g.played2)::int, 0) AS played2,ARRAY_TO_STRING(g.hand1, '') AS hand1,REGEXP_REPLACE(ARRAY_TO_STRING(g.hand2, ''), '.', '?', 'g') AS hand2, g.letters AS letters, /* is a varchar[15][15] */g.values AS values, /* is an integer[15][15] */g.bid AS bid,m.tiles AS last_tiles,m.score AS last_scoreFROM words_games g LEFT JOIN words_moves m USING(mid)WHERE g.player1 = in_uidUNION SELECTg.gid AS gid,EXTRACT(EPOCH FROM g.created)::int AS created,g.player2 AS player1,COALESCE(g.player2, 0) AS player1,COALESCE(EXTRACT(EPOCH FROM g.played2)::int, 0) AS played1,COALESCE(EXTRACT(EPOCH FROM g.played1)::int, 0) AS played2,ARRAY_TO_STRING(g.hand2, '') AS hand1,REGEXP_REPLACE(ARRAY_TO_STRING(g.hand1, ''), '.', '?', 'g') AS hand2, g.letters AS letters,g.values AS values,g.bid AS bid,m.tiles AS last_tiles,m.score AS last_scoreFROM words_games g LEFT JOIN words_moves m USING(mid)WHERE g.player2 = in_uid;END$func$ LANGUAGE plpgsql;but calling it gives me errors:words=> select * from words_select_games(1);ERROR: a column definition list is required for functions returning "record"LINE 1: select * from words_select_games(1);^words=> select gid, bid from words_select_games(1); ERROR: a column definition list is required for functions returning "record"LINE 1: select gid, bid from words_select_games(1);^I have also unsuccessfully triedRETURNS SETOF words_games, words_moves ASand without the comma:RETURNS SETOF words_games words_moves ASHow would you recommend to fix my declaration problem please?
RegardsAlex
On Fri, Aug 26, 2016 at 10:50 AM, Cachique <cachique@gmail.com> wrote: > Hi > From the documentation... ( > https://www.postgresql.org/docs/current/static/sql-select.html ) > > 'Function calls can appear in the FROM clause. (This is especially useful > for functions that return result sets, but any function can be used.) This > acts as though the function's output were created as a temporary table for > the duration of this single SELECT command... > If the function has been defined as returning the record data type, then an > alias or the key word AS must be present, followed by a column definition > list in the form ( column_name data_type [, ... ]). The column definition > list must match the actual number and types of columns returned by the > function.' > > > You need to use 'returns table' syntax or to add an alias in your query. > Something like > select * from words_select_games(1) as (gid type, created type, player1 > type, ...); > > Check for the correct column types In this case it's probably better to have the function return a table type, RETURNS table, or define the output with OUT variables. The record defining syntax at time of query: SELECT foo() AS (a int, b text); Typically should only be used when the function does not have a well defined return structure. dlbink is a good example, since the structure of the input query directly controls the stucture of the return type. The sever has no way to deal with that when the query is parsed and planned, so you have to help it out. merlin merlin
On 8/26/16 10:32 AM, Alexander Farber wrote: > Thank you, I was just wondering if there is a simpler way... but ok It would be nice if there was a way to pass dynamically formed records around, similar to how you can pass the results of row() around. Someone else has actually be asking about this at https://github.com/decibel/pg_lambda/issues/1. BTW, there's no advantage I can think of to using plpgsql just to return the output of a query. You'd be better off using a SQL function instead. Actually, I guess the plan for the plpgsql version would be cached; I'm not sure if the same is true for SQL functions. But you'd probably need a more complex query for that to be a win over the lighter weight nature of SQL functions. -- Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX Experts in Analytics, Data Architecture and PostgreSQL Data in Trouble? Get it in Treble! http://BlueTreble.com 855-TREBLE2 (855-873-2532) mobile: 512-569-9461
2016-08-29 1:59 GMT+02:00 Jim Nasby <Jim.Nasby@bluetreble.com>:
On 8/26/16 10:32 AM, Alexander Farber wrote:Thank you, I was just wondering if there is a simpler way... but ok
It would be nice if there was a way to pass dynamically formed records around, similar to how you can pass the results of row() around. Someone else has actually be asking about this at https://github.com/decibel/pg_lambda/issues/1.
Probably there is a space to be PLpgSQL more flexible - but there are limits - PLpgSQL is black box for SQL engine, and when output is any record type, then SQL engine knows zero about returning data structure in preprocessing time. It is reason, why exists statement CALL (not implemented in Postgres) - where these information should not be necessary.
BTW, there's no advantage I can think of to using plpgsql just to return the output of a query. You'd be better off using a SQL function instead.
Actually, I guess the plan for the plpgsql version would be cached; I'm not sure if the same is true for SQL functions. But you'd probably need a more complex query for that to be a win over the lighter weight nature of SQL functions.
--
Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX
Experts in Analytics, Data Architecture and PostgreSQL
Data in Trouble? Get it in Treble! http://BlueTreble.com
855-TREBLE2 (855-873-2532) mobile: 512-569-9461
--
Sent via pgsql-general mailing list (pgsql-general@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-general
Re: a column definition list is required for functions returning "record"
From
Alexander Farber
Date:
Thank you for your comments!
I have switched to SQL function now
(I didn't realize it is better performancewise) -
I have switched to SQL function now
(I didn't realize it is better performancewise) -
CREATE OR REPLACE FUNCTION words_select_games(IN in_uid integer)
RETURNS TABLE(
out_gid integer,
out_created integer,
out_player1 integer,
out_player2 integer,
out_played1 integer,
out_played2 integer,
out_score1 integer,
out_score2 integer,
out_hand1 text,
out_hand2 text,
out_letters varchar[15][15],
out_values integer[15][15],
out_bid integer,
out_last_tiles jsonb,
out_last_score integer
) AS
$func$
SELECT
g.gid,
EXTRACT(EPOCH FROM g.created)::int,
g.player1,
g.player2, -- can be NULL
EXTRACT(EPOCH FROM g.played1)::int,
EXTRACT(EPOCH FROM g.played2)::int,
g.score1,
g.score2,
ARRAY_TO_STRING(g.hand1, ''),
REGEXP_REPLACE(ARRAY_TO_STRING(g.hand2, ''), '.', '?', 'g'),
g.letters,
g.values,
g.bid,
m.tiles,
m.score
FROM words_games g LEFT JOIN words_moves m USING(mid)
WHERE g.player1 = in_uid
UNION SELECT
g.gid,
EXTRACT(EPOCH FROM g.created)::int,
g.player2,
g.player1, -- can not be NULL
EXTRACT(EPOCH FROM g.played2)::int,
EXTRACT(EPOCH FROM g.played1)::int,
g.score2,
g.score1,
ARRAY_TO_STRING(g.hand2, ''),
REGEXP_REPLACE(ARRAY_TO_STRING(g.hand1, ''), '.', '?', 'g'),
g.letters,
g.values,
g.bid,
m.tiles,
m.score
FROM words_games g LEFT JOIN words_moves m USING(mid)
WHERE g.player2 = in_uid;
$func$ LANGUAGE sql;
RETURNS TABLE(
out_gid integer,
out_created integer,
out_player1 integer,
out_player2 integer,
out_played1 integer,
out_played2 integer,
out_score1 integer,
out_score2 integer,
out_hand1 text,
out_hand2 text,
out_letters varchar[15][15],
out_values integer[15][15],
out_bid integer,
out_last_tiles jsonb,
out_last_score integer
) AS
$func$
SELECT
g.gid,
EXTRACT(EPOCH FROM g.created)::int,
g.player1,
g.player2, -- can be NULL
EXTRACT(EPOCH FROM g.played1)::int,
EXTRACT(EPOCH FROM g.played2)::int,
g.score1,
g.score2,
ARRAY_TO_STRING(g.hand1, ''),
REGEXP_REPLACE(ARRAY_TO_STRING(g.hand2, ''), '.', '?', 'g'),
g.letters,
g.values,
g.bid,
m.tiles,
m.score
FROM words_games g LEFT JOIN words_moves m USING(mid)
WHERE g.player1 = in_uid
UNION SELECT
g.gid,
EXTRACT(EPOCH FROM g.created)::int,
g.player2,
g.player1, -- can not be NULL
EXTRACT(EPOCH FROM g.played2)::int,
EXTRACT(EPOCH FROM g.played1)::int,
g.score2,
g.score1,
ARRAY_TO_STRING(g.hand2, ''),
REGEXP_REPLACE(ARRAY_TO_STRING(g.hand1, ''), '.', '?', 'g'),
g.letters,
g.values,
g.bid,
m.tiles,
m.score
FROM words_games g LEFT JOIN words_moves m USING(mid)
WHERE g.player2 = in_uid;
$func$ LANGUAGE sql;
Pavel Stehule <pavel.stehule@gmail.com> writes: > 2016-08-29 1:59 GMT+02:00 Jim Nasby <Jim.Nasby@bluetreble.com>: >> It would be nice if there was a way to pass dynamically formed records >> around, similar to how you can pass the results of row() around. Someone >> else has actually be asking about this at https://github.com/decibel/pg_ >> lambda/issues/1. > Probably there is a space to be PLpgSQL more flexible - but there are > limits - PLpgSQL is black box for SQL engine, and when output is any record > type, then SQL engine knows zero about returning data structure in > preprocessing time. Exactly. You can pass anonymous record types around today, as long as you don't do anything that requires knowing what their contents are, either in the function or in the calling query: regression=# create function foor(int,int) returns record language sql as $$ select row($1,$2); $$; CREATE FUNCTION regression=# select foor(23,45); foor --------- (23,45) (1 row) regression=# create function plr(int,int) returns record language plpgsql as $$begin return row($1,$2); end; $$; CREATE FUNCTION regression=# select plr(23,45); plr --------- (23,45) (1 row) What you can't do is, eg, regression=# select * from plr(23,45); ERROR: a column definition list is required for functions returning "record" LINE 1: select * from plr(23,45); ^ because the parser has no basis on which to expand the "*". The column definition list is exactly a hack for telling it that. regards, tom lane
On Mon, Aug 29, 2016 at 6:28 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote: > Pavel Stehule <pavel.stehule@gmail.com> writes: >> 2016-08-29 1:59 GMT+02:00 Jim Nasby <Jim.Nasby@bluetreble.com>: >>> It would be nice if there was a way to pass dynamically formed records >>> around, similar to how you can pass the results of row() around. Someone >>> else has actually be asking about this at https://github.com/decibel/pg_ >>> lambda/issues/1. > >> Probably there is a space to be PLpgSQL more flexible - but there are >> limits - PLpgSQL is black box for SQL engine, and when output is any record >> type, then SQL engine knows zero about returning data structure in >> preprocessing time. > > Exactly. You can pass anonymous record types around today, as long as you > don't do anything that requires knowing what their contents are, either in > the function or in the calling query: > > regression=# create function foor(int,int) returns record language sql as $$ select row($1,$2); $$; > CREATE FUNCTION > regression=# select foor(23,45); > foor > --------- > (23,45) > (1 row) > > regression=# create function plr(int,int) returns record language plpgsql as $$begin return row($1,$2); end; $$; > CREATE FUNCTION > regression=# select plr(23,45); > plr > --------- > (23,45) > (1 row) > > What you can't do is, eg, > > regression=# select * from plr(23,45); > ERROR: a column definition list is required for functions returning "record" > LINE 1: select * from plr(23,45); Another tactic is to use json in such cases: create function fooj(int,int) returns json language sql as $$ select to_json((select q from (select $1 as a, $2 as b) q)); $$; CREATE FUNCTION postgres=# select fooj(3,4); fooj ─────────────── {"a":3,"b":4} (1 row) the advantage here is that you're not quite as boxed in: stuff like postgres=# select * from json_each(fooj(3,4)); key │ value ─────┼─────── a │ 3 b │ 4 ... in the json api (which is rich and getting richer) can be used to work around the anonymous row limitations. merlin
On 8/29/16 6:28 AM, Tom Lane wrote: > Pavel Stehule <pavel.stehule@gmail.com> writes: >> > 2016-08-29 1:59 GMT+02:00 Jim Nasby <Jim.Nasby@bluetreble.com>: >>> >> It would be nice if there was a way to pass dynamically formed records >>> >> around, similar to how you can pass the results of row() around. Someone >>> >> else has actually be asking about this at https://github.com/decibel/pg_ >>> >> lambda/issues/1. >> > Probably there is a space to be PLpgSQL more flexible - but there are >> > limits - PLpgSQL is black box for SQL engine, and when output is any record >> > type, then SQL engine knows zero about returning data structure in >> > preprocessing time. > Exactly. You can pass anonymous record types around today, as long as you > don't do anything that requires knowing what their contents are, either in > the function or in the calling query: What I was thinking of is something (like a function) that has explicitly defined what the contents of the record are. -- Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX Experts in Analytics, Data Architecture and PostgreSQL Data in Trouble? Get it in Treble! http://BlueTreble.com 855-TREBLE2 (855-873-2532) mobile: 512-569-9461
Fri, Sep 2, 2016 at 6:55 PM, Jim Nasby <Jim.Nasby@bluetreble.com> wrote: > On 8/29/16 6:28 AM, Tom Lane wrote: >> >> Pavel Stehule <pavel.stehule@gmail.com> writes: >>> >>> > 2016-08-29 1:59 GMT+02:00 Jim Nasby <Jim.Nasby@bluetreble.com>: >>>> >>>> >> It would be nice if there was a way to pass dynamically formed >>>> >> records >>>> >> around, similar to how you can pass the results of row() around. >>>> >> Someone >>>> >> else has actually be asking about this at >>>> >> https://github.com/decibel/pg_ >>>> >> lambda/issues/1. >>> >>> > Probably there is a space to be PLpgSQL more flexible - but there are >>> > limits - PLpgSQL is black box for SQL engine, and when output is any >>> > record >>> > type, then SQL engine knows zero about returning data structure in >>> > preprocessing time. >> >> Exactly. You can pass anonymous record types around today, as long as you >> don't do anything that requires knowing what their contents are, either in >> the function or in the calling query: > > What I was thinking of is something (like a function) that has explicitly > defined what the contents of the record are. We have that already, it's named 'json_each_text' :-). merlin
On 9/7/16 2:02 PM, Merlin Moncure wrote: >>> >> Exactly. You can pass anonymous record types around today, as long as you >>> >> don't do anything that requires knowing what their contents are, either in >>> >> the function or in the calling query: >> > >> > What I was thinking of is something (like a function) that has explicitly >> > defined what the contents of the record are. > We have that already, it's named 'json_each_text' Apparently you haven't looked at json parse/deparse costs ;P -- Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX Experts in Analytics, Data Architecture and PostgreSQL Data in Trouble? Get it in Treble! http://BlueTreble.com 855-TREBLE2 (855-873-2532) mobile: 512-569-9461
Jim Nasby <Jim.Nasby@BlueTreble.com> writes: > On 9/7/16 2:02 PM, Merlin Moncure wrote: >> We have that already, it's named 'json_each_text' > Apparently you haven't looked at json parse/deparse costs ;P Well, a PL function is gonna be none too cheap either. Using something like JSON definitely has lots to recommend it --- eg, it probably won't break when you find out your initial spec for the transport format was too simplistic. regards, tom lane