RETURN QUERY EXECUTE
was introduced with Postgres 8.4.
Your version is just too old and unsupported by now. Upgrade to a more recent version.
Also, dynamic column names in the result are very hard to come by. It's a principle of SQL that it wants to know the return type - including the names - up front.
Returning anonymous records without a column definition list only works for a single record. Else you have to provide a column definition list in the call. Details under his question on SO:
Return multiple fields as a record in PostgreSQL with PL/pgSQL
There are limited ways around this with with polymorphic types. Advanced stuff:
Refactor a PL/pgSQL function to return the output of various SELECT queries
BTW, your function would look like this in modern PL/pgSQL:
CREATE OR REPLACE FUNCTION get_policy_name (
_id int,
_lang text,
_def_value text
) RETURNS TABLE (col text) AS
$func$
BEGIN
RETURN QUERY EXECUTE
format('SELECT COALESCE(%I, col, $1)
FROM my_table WHERE id = $2'
, 'col_' || _lang)
USING _def_value, _id;
END
$func$ LANGUAGE plpgsql;
Call:
SELECT col AS col_fr FROM get_policy_name (1, 'fr', 'foo');
Here, I am simply using a column alias in the call to achieve what you want. Much easier than dynamic column names ...
It's a textbook case for a CTE, like @Daniel commented.
The example can be simplified some more. And you need to be aware of how LIMIT
works in a UNION
query.
CREATE OR REPLACE FUNCTION featured_products(valid_to_in timestamp
, taxonomy_id_in integer
, product_limit_in integer)
RETURNS SETOF integer AS
$func$
BEGIN
RETURN QUERY
WITH featured AS (SELECT supplier_id FROM products LIMIT 2)
SELECT supplier_id
FROM featured
UNION ALL
(
SELECT p.supplier_id
FROM products p
LEFT JOIN featured f USING (supplier_id)
WHERE f.supplier_id IS NULL
LIMIT product_limit_in
) -- parens required - or not?
END
$func$ LANGUAGE plpgsql VOLATILE;
LIMIT
can only be applied once in a UNION
(ALL
) query, unless you enclose the leg of the query in parentheses. You may or may not want to add parentheses.
- The way I have it, a maximum of
product_limit_in
rows are returned in addition to the "featured" rows from the CTE.
- If you remove the parentheses you get a maximum of
product_limit_in
rows total - meaning that even "featured" products may be discarded.
Related: Optimize a query on two big tables
Either way, don't ORDER BY
the outer (combined) result before you LIMIT
, if you can avoid it. Postgres can optimize the query very efficiently and just stop evaluating once enough rows have been returned (possibly fetching tuples from the top of a matching index). That would not be possible any more, which can make a huge difference in performance.
Using LEFT JOIN / NOT NULL
to exclude featured rows from the second SELECT, which is probably faster than NOT IN
and does not carry "surprises" when dealing with NULL values or empty results.
In Postgres (as opposed to some other RDBMS), you can refer to p.supplier_id
and f.supplier_id
after joining with USING (supplier_id)
.
And yes, the CTE is only evaluated once:
A useful property of WITH
queries is that they are evaluated only once
per execution of the parent query, even if they are referred to more
than once by the parent query or sibling WITH
queries.
Bold emphasis mine.
Best Answer
In PostgreSQL 9.5 and newer you could use row-level security to do this. I don't think it's a great idea, but that's the way to approach it if you're going to attempt it.