16

I have a Postgres function which is returning a table:

CREATE OR REPLACE FUNCTION testFunction() RETURNS TABLE(a int, b int) AS
$BODY$
DECLARE a int DEFAULT 0;
DECLARE b int DEFAULT 0;
BEGIN
CREATE TABLE tempTable AS SELECT a, b;
RETURN QUERY SELECT * FROM tempTable; 
DROP TABLE tempTable;
END;
$BODY$
LANGUAGE plpgsql;

This function is not returning data in row and column form. Instead it returns data as:

(0,0)

That is causing a problem in Coldfusion cfquery block in extracting data. How do I get data in rows and columns when a table is returned from this function? In other words: Why does the PL/pgSQL function not return data as columns?

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Satish Sharma
  • 3,284
  • 9
  • 38
  • 51

2 Answers2

33

To get individual columns instead of the row type, call the function with:

SELECT * FROM testfunction();

Just like you would select all columns from a table.
Also consider this reviewed form of your test function:

CREATE OR REPLACE FUNCTION testfunction()
  RETURNS TABLE(a int, b int)
  LANGUAGE plpgsql AS
$func$
DECLARE
   _a int := 0;
   _b int := 0;
BEGIN
   CREATE TABLE tempTable AS SELECT _a, _b;
   RETURN QUERY SELECT * FROM tempTable;
   DROP TABLE tempTable;
END
$func$;

In particular:

The DECLARE key word is only needed once.

Avoid declaring parameters that are already (implicitly) declared as OUT parameters in the RETURNS TABLE (...) clause.

Don't use unquoted CaMeL-case identifiers in Postgres. It works, unquoted identifiers are cast to lower case, but it can lead to confusing errors. See:

The temporary table in the example is completely useless (probably over-simplified). The example as given boils down to:

CREATE OR REPLACE FUNCTION testfunction(OUT a int, OUT b int)
  LANGUAGE plpgsql AS
$func$
BEGIN
   a := 0;
   b := 0;
END
$func$;
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • PostgreSQL 12's [documentation](https://www.postgresql.org/docs/current/plpgsql-control-structures.html#PLPGSQL-STATEMENTS-RETURNING) advocates an additional `RETURN;` to be explicit about when you are finished with building the output accumulatively. – Rafs Jul 31 '20 at 16:16
  • 1
    @JabroJacob: The manual also states (at said location): `This restriction does not apply to functions with output parameters and functions returning void, however. In those cases a RETURN statement is automatically executed if the top-level block finishes.` You can add `RETURN` to be explicit, but it's noise here. – Erwin Brandstetter Jul 31 '20 at 16:43
  • What if the table has more than 10 columns? – TheRealChx101 Aug 14 '20 at 19:47
  • @TheRealChx101: What's the significance of the number 10? – Erwin Brandstetter Aug 20 '20 at 23:31
  • It's just a random number with no significance. I was looking for a solution in the case where you had a lot of columns. I believe you already answered that question here: https://stackoverflow.com/a/8611675/1219213 – TheRealChx101 Aug 21 '20 at 00:08
6

Of course you can do this by putting the function call in the FROM clause, like Eric Brandstetter correctly answered. However, this is sometimes complicating in a query that already has other things in the FROM clause. To get the individual columns that the function returns, you can use this syntax:

SELECT (testfunction()).*

Or to get only the column called "a":

SELECT (testfunction()).a

Place the whole function, including the input value(s) in parenteses, followed by a dot and the desired column name, or an asterisk.

To get the column names that the function returns, you'll have to either:

  • check the source code
  • inspect the result of the function first, like so : SELECT * FROM testfunction() .

The input values can still come out of a FROM clause. Just to illustrate this, consider this function and test data:

CREATE FUNCTION funky(a integer, b integer)
RETURNS TABLE(x double precision, y double precision) AS $$
 SELECT a*random(), b*random();
$$ LANGUAGE SQL;

CREATE TABLE mytable(a integer, b integer);
INSERT INTO mytable
    SELECT generate_series(1,100), generate_series(101,200);

You could call the function "funky(a,b)", without the need to put it in the FROM clause:

SELECT (funky(mytable.a, mytable.b)).*
FROM mytable;

Which would result in 2 columns:

         x         |         y         
-------------------+-------------------
 0.202419687062502 |   55.417385618668
  1.97231830470264 |  63.3628275180236
  1.89781916560605 |  1.98870931006968
(...)
wbloos
  • 231
  • 3
  • 3
  • 1
    Unnesting the result of a set-returning in the `SELECT` list directly might seem like a smart shorthand. But if the function returns multiple columns (like in the case at hand), it results in the function being evaluated multiple times. More expensive, can have unintended side effects, and can even produce surprising results with volatile functions. Typically, you'd want none of this.See: https://stackoverflow.com/a/28853666/939860 and https://stackoverflow.com/a/18370271/939860. – Erwin Brandstetter Aug 01 '20 at 11:04