If you want to use a function to process result sets from a query, your best option is to SELECT ... INTO TEMPORARY TABLE
then call the function with the temporary table name.
It's annoyingly difficult to consume a rowset in a PostgreSQL function given how trivial rowsets are to produce. The only ways I'm aware of to do it are by consuming a refcursor, processing a temporary table, or by implementing an aggregate or window function. The latter two options don't allow you control over how many rows get returned, so they aren't suitable for your purposes.
Functions cannot refer to common table expression aliases in CTEs that call the function, so you can't use a CTE to create a virtual table and pass the name of the table to the function. Example showing it doesn't work:
CREATE OR REPLACE FUNCTION dynsql(tname text, colname text) RETURNS SETOF RECORD AS
$$
BEGIN
RETURN QUERY EXECUTE format('SELECT %I FROM %I', colname, tname);
END;
$$ LANGUAGE plpgsql;
WITH dummy(col) AS (VALUES (1),(2),(3))
SELECT * FROM dynsql('dummy','col') t(id integer);
Result:
ERROR: relation "dummy" does not exist
... because the alias in a WITH
expression is local to the WITH
expression. (It'd be nice to be able to refer to it from functions, but that'd also create all sorts of exciting name clash problems and security issues with SECURITY DEFINER
functions.)
While you can write a PL/PgSQL function that consumes a refcursor, this requires you to DECLARE
a cursor with a query and pass it to the function. You can't just use ordinary function call syntax. It's also quite inefficient and requires LOOP
ing in the function. I don't think it'd be much help.
When implementing the function, use EXECUTE format(...) USING ...
to keep the dynamic SQL not-too-horrible. See this earlier answer.