aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/executor/execQual.c34
-rw-r--r--src/backend/executor/functions.c6
-rw-r--r--src/test/regress/expected/rangefuncs.out67
-rw-r--r--src/test/regress/sql/rangefuncs.sql34
4 files changed, 131 insertions, 10 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index a0e4566630b..b1bd9e4e7e1 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.249 2009/06/11 14:48:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.250 2009/06/11 17:25:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1089,9 +1089,15 @@ init_fcache(Oid foid, FuncExprState *fcache,
fcache->funcResultDesc = tupdesc;
fcache->funcReturnsTuple = false;
}
+ else if (functypclass == TYPEFUNC_RECORD)
+ {
+ /* This will work if function doesn't need an expectedDesc */
+ fcache->funcResultDesc = NULL;
+ fcache->funcReturnsTuple = true;
+ }
else
{
- /* Else, we will complain if function wants materialize mode */
+ /* Else, we will fail if function needs an expectedDesc */
fcache->funcResultDesc = NULL;
}
@@ -1252,18 +1258,32 @@ ExecPrepareTuplestoreResult(FuncExprState *fcache,
if (fcache->funcResultSlot == NULL)
{
/* Create a slot so we can read data out of the tuplestore */
+ TupleDesc slotDesc;
MemoryContext oldcontext;
- /* We must have been able to determine the result rowtype */
- if (fcache->funcResultDesc == NULL)
+ oldcontext = MemoryContextSwitchTo(fcache->func.fn_mcxt);
+
+ /*
+ * If we were not able to determine the result rowtype from context,
+ * and the function didn't return a tupdesc, we have to fail.
+ */
+ if (fcache->funcResultDesc)
+ slotDesc = fcache->funcResultDesc;
+ else if (resultDesc)
+ {
+ /* don't assume resultDesc is long-lived */
+ slotDesc = CreateTupleDescCopy(resultDesc);
+ }
+ else
+ {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function returning setof record called in "
"context that cannot accept type record")));
+ slotDesc = NULL; /* keep compiler quiet */
+ }
- oldcontext = MemoryContextSwitchTo(fcache->func.fn_mcxt);
- fcache->funcResultSlot =
- MakeSingleTupleTableSlot(fcache->funcResultDesc);
+ fcache->funcResultSlot = MakeSingleTupleTableSlot(slotDesc);
MemoryContextSwitchTo(oldcontext);
}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index b0d8b9008a8..fe25798a21e 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.134 2009/06/11 14:48:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.135 2009/06/11 17:25:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -634,11 +634,11 @@ fmgr_sql(PG_FUNCTION_ARGS)
* For simplicity, we require callers to support both set eval modes.
* There are cases where we must use one or must use the other, and
* it's not really worthwhile to postpone the check till we know.
+ * But note we do not require caller to provide an expectedDesc.
*/
if (!rsi || !IsA(rsi, ReturnSetInfo) ||
(rsi->allowedModes & SFRM_ValuePerCall) == 0 ||
- (rsi->allowedModes & SFRM_Materialize) == 0 ||
- rsi->expectedDesc == NULL)
+ (rsi->allowedModes & SFRM_Materialize) == 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("set-valued function called in context that cannot accept a set")));
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index a5cff8ca43a..486dd3f3fe0 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -763,3 +763,70 @@ select t.a, t, t.a from foo1(10000) t limit 1;
(1 row)
drop function foo1(n integer);
+-- test use of SQL functions returning record
+-- this is supported in some cases where the query doesn't specify
+-- the actual record type ...
+create function array_to_set(anyarray) returns setof record as $$
+ select i AS "index", $1[i] AS "value" from generate_subscripts($1, 1) i
+$$ language sql strict immutable;
+select array_to_set(array['one', 'two']);
+ array_to_set
+--------------
+ (1,one)
+ (2,two)
+(2 rows)
+
+select * from array_to_set(array['one', 'two']) as t(f1 int,f2 text);
+ f1 | f2
+----+-----
+ 1 | one
+ 2 | two
+(2 rows)
+
+select * from array_to_set(array['one', 'two']); -- fail
+ERROR: a column definition list is required for functions returning "record"
+LINE 1: select * from array_to_set(array['one', 'two']);
+ ^
+create temp table foo(f1 int8, f2 int8);
+create function testfoo() returns record as $$
+ insert into foo values (1,2) returning *;
+$$ language sql;
+select testfoo();
+ testfoo
+---------
+ (1,2)
+(1 row)
+
+select * from testfoo() as t(f1 int8,f2 int8);
+ f1 | f2
+----+----
+ 1 | 2
+(1 row)
+
+select * from testfoo(); -- fail
+ERROR: a column definition list is required for functions returning "record"
+LINE 1: select * from testfoo();
+ ^
+drop function testfoo();
+create function testfoo() returns setof record as $$
+ insert into foo values (1,2), (3,4) returning *;
+$$ language sql;
+select testfoo();
+ testfoo
+---------
+ (1,2)
+ (3,4)
+(2 rows)
+
+select * from testfoo() as t(f1 int8,f2 int8);
+ f1 | f2
+----+----
+ 1 | 2
+ 3 | 4
+(2 rows)
+
+select * from testfoo(); -- fail
+ERROR: a column definition list is required for functions returning "record"
+LINE 1: select * from testfoo();
+ ^
+drop function testfoo();
diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql
index 3c68f0aa447..3727a36aaff 100644
--- a/src/test/regress/sql/rangefuncs.sql
+++ b/src/test/regress/sql/rangefuncs.sql
@@ -351,3 +351,37 @@ reset work_mem;
select t.a, t, t.a from foo1(10000) t limit 1;
drop function foo1(n integer);
+
+-- test use of SQL functions returning record
+-- this is supported in some cases where the query doesn't specify
+-- the actual record type ...
+
+create function array_to_set(anyarray) returns setof record as $$
+ select i AS "index", $1[i] AS "value" from generate_subscripts($1, 1) i
+$$ language sql strict immutable;
+
+select array_to_set(array['one', 'two']);
+select * from array_to_set(array['one', 'two']) as t(f1 int,f2 text);
+select * from array_to_set(array['one', 'two']); -- fail
+
+create temp table foo(f1 int8, f2 int8);
+
+create function testfoo() returns record as $$
+ insert into foo values (1,2) returning *;
+$$ language sql;
+
+select testfoo();
+select * from testfoo() as t(f1 int8,f2 int8);
+select * from testfoo(); -- fail
+
+drop function testfoo();
+
+create function testfoo() returns setof record as $$
+ insert into foo values (1,2), (3,4) returning *;
+$$ language sql;
+
+select testfoo();
+select * from testfoo() as t(f1 int8,f2 int8);
+select * from testfoo(); -- fail
+
+drop function testfoo();