Create a function that creates a function which returns a table and return that table in one step

From: Rainer Schuetz <rs(at)bagong(dot)de>
To: pgsql-novice(at)postgresql(dot)org
Subject: Create a function that creates a function which returns a table and return that table in one step
Date: 2008-11-21 16:53:52
Message-ID: 20821234-C0FC-4982-9D84-EE27D2211509@bagong.de
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-novice

Hello,

I hope this is the right place to post my question, it is a user-
problem and I wasn't able to extract a solution from the
documentation. I am using PostgreSQL 8.35 I am a real novice in the
field. I am running my SQL-queries in pgAdmin and create plpgsql-
functions from there as well. I hope I can make my problem understood:

I have a function (the 'outer' function) that creates another function
which runs a select-query on a table. This table is specified by a
parameter I use when running the outer function (there are many
tables, on which the query could be executed and I would like to
prevent having to write my function for each of them). The outer
function concatenates a query-string by processing parameters I
specify when calling the function. As said above one of this
parameters is the table-name. Within the outer function I use the
parameter-alias 'tablename' to specify the return-type in the created
function that returns the table (tablename%rowtype), and as the
variable is expanded before the function is created, a working 'inner'
function results, where the name of the queried table and return-type
fit one-another. As the outer function returns nothing (for now it
only creates the function) I specify return-type void.

What I would like to do is not only create the 'inner' function from
within the 'outer' function, but run it as well so that the table
produced by the inner function is returned right away. From my
(minimal) understanding this requires that I specify a return-type in
the outer function - yet this return-type could be different each time
I run the function because the called table as specified by a
parameter could be different each time I create the inner function. If
I try to use the parameter-alias 'tablename' like in the inner
function (tablename%rowtype) I do get an error:

ERROR: relation "tablename" does not exist
Or:
ERROR: type "tablename" does not exist

If I use SETOF record as return type and declare a variable of the
datatype record for the RETURN statement, the function interpreter
accepts my function, but when I run the function, I get the error
message:

ERROR: set-valued function called in context that cannot accept a set
CONTEXT: PL/pgSQL function "test" line 46 at RETURN NEXT

I tried around with polymorph returntypes like anyelement, but had no
luck. Does anybody have an idea, what I am doing wrong and how I
should correct this?

I attach the original function, it is quite intransparent, yet hope
with above explanation things might be understandable. It is not the
inner select-query that causes problems, although it looks strange
with all the ''' and concatenation symbols. I can run the resulting
inner function manually without problems.

Thank you for any help

Best
Rainer

-----

CREATE OR REPLACE FUNCTION test(tablename text, slpl text, frm text,
div text, ost numeric)
RETURNS SETOF record AS $BODY$

DECLARE
createF text;
runF text;
resultData record;

BEGIN
-- prepare inner function:
createF := 'CREATE OR REPLACE FUNCTION FoO() RETURNS SETOF '||
tablename||' AS $$
DECLARE
sqlQuery varchar;
output '||tablename||'%rowtype;

BEGIN
sqlQuery := '' SELECT * FROM '||tablename||'
WHERE pathet ~ '||''''''||slpl||''''''||'
AND structure ~ '||''''''||frm||''''''||'
AND (substring(phase::text FROM
char_length(phase::text)-7)::numeric + '||ost||') % '||div||' = 0'';
FOR output IN EXECUTE sqlQuery LOOP
RETURN NEXT output;
END LOOP;
RETURN;
END;$$language plpgsql';
-- end inner function definition

-- create inner function (works):
DROP FUNCTION foo();
EXECUTE createF;

-- attempt to run inner function (fails):
FOR resultData IN SELECT * FROM foo() LOOP
RETURN NEXT resultData;
END LOOP;
RETURN;

END$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100;

Responses

Browse pgsql-novice by date

  From Date Subject
Next Message Tom Lane 2008-11-21 17:18:40 Re: Create a function that creates a function which returns a table and return that table in one step
Previous Message Tom Lane 2008-11-20 16:20:16 Re: date range query help