Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit0c19f05

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 lessaccidentally, but I had broken it while extending the code to allowmaterialize-mode functions to be called in select lists. Add a regressiontest case so it doesn't get broken again. Per gripe from Greg Davidson.
1 parent772a074 commit0c19f05

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/1114:48:57 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.250 2009/06/1117: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+
elseif (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 willcomplain if functionwants materialize mode */
1100+
/* Else, we willfail if functionneeds 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+
TupleDescslotDesc;
12551262
MemoryContextoldcontext;
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+
elseif (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/1114:48:57 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.135 2009/06/1117: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
selectt.a, t,t.afrom foo1(10000) tlimit1;
352352

353353
dropfunction foo1(ninteger);
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+
createfunctionarray_to_set(anyarray) returns setof recordas $$
360+
select iAS"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(f1int,f2text);
365+
select*from array_to_set(array['one','two']);-- fail
366+
367+
create temp table foo(f1 int8, f2 int8);
368+
369+
createfunctiontestfoo() returns recordas $$
370+
insert into foovalues (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+
dropfunction testfoo();
378+
379+
createfunctiontestfoo() returns setof recordas $$
380+
insert into foovalues (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+
dropfunction testfoo();

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp