Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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

+27-7
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

+3-3
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

+67
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

+34
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)