Skip to content

Commit 0c19f05

Browse files
committed
Fix things so that you can still do "select foo()" where foo is a SQL
function returning setof record. This used to work, more or less accidentally, but I had broken it while extending the code to allow materialize-mode functions to be called in select lists. Add a regression test case so it doesn't get broken again. Per gripe from Greg Davidson.
1 parent 772a074 commit 0c19f05

File tree

4 files changed

+131
-10
lines changed

4 files changed

+131
-10
lines changed

src/backend/executor/execQual.c

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.249 2009/06/11 14:48:57 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.250 2009/06/11 17:25:38 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1089,9 +1089,15 @@ init_fcache(Oid foid, FuncExprState *fcache,
10891089
fcache->funcResultDesc = tupdesc;
10901090
fcache->funcReturnsTuple = false;
10911091
}
1092+
else if (functypclass == TYPEFUNC_RECORD)
1093+
{
1094+
/* This will work if function doesn't need an expectedDesc */
1095+
fcache->funcResultDesc = NULL;
1096+
fcache->funcReturnsTuple = true;
1097+
}
10921098
else
10931099
{
1094-
/* Else, we will complain if function wants materialize mode */
1100+
/* Else, we will fail if function needs an expectedDesc */
10951101
fcache->funcResultDesc = NULL;
10961102
}
10971103

@@ -1252,18 +1258,32 @@ ExecPrepareTuplestoreResult(FuncExprState *fcache,
12521258
if (fcache->funcResultSlot == NULL)
12531259
{
12541260
/* Create a slot so we can read data out of the tuplestore */
1261+
TupleDesc slotDesc;
12551262
MemoryContext oldcontext;
12561263

1257-
/* We must have been able to determine the result rowtype */
1258-
if (fcache->funcResultDesc == NULL)
1264+
oldcontext = MemoryContextSwitchTo(fcache->func.fn_mcxt);
1265+
1266+
/*
1267+
* If we were not able to determine the result rowtype from context,
1268+
* and the function didn't return a tupdesc, we have to fail.
1269+
*/
1270+
if (fcache->funcResultDesc)
1271+
slotDesc = fcache->funcResultDesc;
1272+
else if (resultDesc)
1273+
{
1274+
/* don't assume resultDesc is long-lived */
1275+
slotDesc = CreateTupleDescCopy(resultDesc);
1276+
}
1277+
else
1278+
{
12591279
ereport(ERROR,
12601280
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12611281
errmsg("function returning setof record called in "
12621282
"context that cannot accept type record")));
1283+
slotDesc = NULL; /* keep compiler quiet */
1284+
}
12631285

1264-
oldcontext = MemoryContextSwitchTo(fcache->func.fn_mcxt);
1265-
fcache->funcResultSlot =
1266-
MakeSingleTupleTableSlot(fcache->funcResultDesc);
1286+
fcache->funcResultSlot = MakeSingleTupleTableSlot(slotDesc);
12671287
MemoryContextSwitchTo(oldcontext);
12681288
}
12691289

src/backend/executor/functions.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.134 2009/06/11 14:48:57 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.135 2009/06/11 17:25:38 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -634,11 +634,11 @@ fmgr_sql(PG_FUNCTION_ARGS)
634634
* For simplicity, we require callers to support both set eval modes.
635635
* There are cases where we must use one or must use the other, and
636636
* it's not really worthwhile to postpone the check till we know.
637+
* But note we do not require caller to provide an expectedDesc.
637638
*/
638639
if (!rsi || !IsA(rsi, ReturnSetInfo) ||
639640
(rsi->allowedModes & SFRM_ValuePerCall) == 0 ||
640-
(rsi->allowedModes & SFRM_Materialize) == 0 ||
641-
rsi->expectedDesc == NULL)
641+
(rsi->allowedModes & SFRM_Materialize) == 0)
642642
ereport(ERROR,
643643
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
644644
errmsg("set-valued function called in context that cannot accept a set")));

src/test/regress/expected/rangefuncs.out

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,3 +763,70 @@ select t.a, t, t.a from foo1(10000) t limit 1;
763763
(1 row)
764764

765765
drop function foo1(n integer);
766+
-- test use of SQL functions returning record
767+
-- this is supported in some cases where the query doesn't specify
768+
-- the actual record type ...
769+
create function array_to_set(anyarray) returns setof record as $$
770+
select i AS "index", $1[i] AS "value" from generate_subscripts($1, 1) i
771+
$$ language sql strict immutable;
772+
select array_to_set(array['one', 'two']);
773+
array_to_set
774+
--------------
775+
(1,one)
776+
(2,two)
777+
(2 rows)
778+
779+
select * from array_to_set(array['one', 'two']) as t(f1 int,f2 text);
780+
f1 | f2
781+
----+-----
782+
1 | one
783+
2 | two
784+
(2 rows)
785+
786+
select * from array_to_set(array['one', 'two']); -- fail
787+
ERROR: a column definition list is required for functions returning "record"
788+
LINE 1: select * from array_to_set(array['one', 'two']);
789+
^
790+
create temp table foo(f1 int8, f2 int8);
791+
create function testfoo() returns record as $$
792+
insert into foo values (1,2) returning *;
793+
$$ language sql;
794+
select testfoo();
795+
testfoo
796+
---------
797+
(1,2)
798+
(1 row)
799+
800+
select * from testfoo() as t(f1 int8,f2 int8);
801+
f1 | f2
802+
----+----
803+
1 | 2
804+
(1 row)
805+
806+
select * from testfoo(); -- fail
807+
ERROR: a column definition list is required for functions returning "record"
808+
LINE 1: select * from testfoo();
809+
^
810+
drop function testfoo();
811+
create function testfoo() returns setof record as $$
812+
insert into foo values (1,2), (3,4) returning *;
813+
$$ language sql;
814+
select testfoo();
815+
testfoo
816+
---------
817+
(1,2)
818+
(3,4)
819+
(2 rows)
820+
821+
select * from testfoo() as t(f1 int8,f2 int8);
822+
f1 | f2
823+
----+----
824+
1 | 2
825+
3 | 4
826+
(2 rows)
827+
828+
select * from testfoo(); -- fail
829+
ERROR: a column definition list is required for functions returning "record"
830+
LINE 1: select * from testfoo();
831+
^
832+
drop function testfoo();

src/test/regress/sql/rangefuncs.sql

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,3 +351,37 @@ reset work_mem;
351351
select t.a, t, t.a from foo1(10000) t limit 1;
352352

353353
drop function foo1(n integer);
354+
355+
-- test use of SQL functions returning record
356+
-- this is supported in some cases where the query doesn't specify
357+
-- the actual record type ...
358+
359+
create function array_to_set(anyarray) returns setof record as $$
360+
select i AS "index", $1[i] AS "value" from generate_subscripts($1, 1) i
361+
$$ language sql strict immutable;
362+
363+
select array_to_set(array['one', 'two']);
364+
select * from array_to_set(array['one', 'two']) as t(f1 int,f2 text);
365+
select * from array_to_set(array['one', 'two']); -- fail
366+
367+
create temp table foo(f1 int8, f2 int8);
368+
369+
create function testfoo() returns record as $$
370+
insert into foo values (1,2) returning *;
371+
$$ language sql;
372+
373+
select testfoo();
374+
select * from testfoo() as t(f1 int8,f2 int8);
375+
select * from testfoo(); -- fail
376+
377+
drop function testfoo();
378+
379+
create function testfoo() returns setof record as $$
380+
insert into foo values (1,2), (3,4) returning *;
381+
$$ language sql;
382+
383+
select testfoo();
384+
select * from testfoo() as t(f1 int8,f2 int8);
385+
select * from testfoo(); -- fail
386+
387+
drop function testfoo();

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy