Search code examples
sqlpostgresqlplpgsql

How to change column names returned from a function?


How to change column names returned from below function?

Executing:

select * from get_parameterbased_return_Table('condition2');

Column names should change to (col3 text, col4 text)

Sample table and function:

CREATE TABLE public.parameterbased_return_table (
    col1 text NULL,
    col2 text NULL,
    col3 int4 NULL,
    col4 int4 NULL
);


INSERT INTO public.parameterbased_return_table (col1, col2, col3, col4)
VALUES
  ('A', 'B', 11, 22)
, ('dfdf', 'dfe', 14, 545)
;


CREATE OR REPLACE FUNCTION get_parameterbased_return_Table(condition TEXT)
RETURNS TABLE (col1 text, col2 text) AS
$$
BEGIN
    IF condition = 'condition1' THEN
        RETURN QUERY SELECT t.col1::text, t.col2::text FROM parameterbased_return_Table t;
    ELSIF condition = 'condition2' THEN
        RETURN QUERY SELECT t.col3::text, t.col4::text FROM parameterbased_return_Table t;
    ELSE
        -- Handle other conditions or return an empty result set
        RETURN QUERY SELECT NULL::record;
    END IF;
END;
$$
LANGUAGE plpgsql;

Solution

  • Postgres (like SQL) is strictly typed. Functions cannot change their return type on the fly - including number, names and types of columns for a function returning a row type.

    There are a limited workarounds, but you have to pass the required information with the function call one or the other way.

    Polymorphic function to change the whole return type

    Here, the trick is to pass the actual return type to the function. See:

    Only makes sense for exotic use cases.
    That said, here is how you pull off such a stunt:

    -- create desired return types
    CREATE TYPE public.result1 AS (col1 text, col2 text);
    CREATE TYPE public.result2 AS (col2 int, col3 int);
    
    -- create polymorphis function
    CREATE OR REPLACE FUNCTION public.get_parameterbased_return_table1(_return_type anyelement)
      RETURNS SETOF anyelement
      LANGUAGE plpgsql AS
    $func$
    BEGIN
       CASE pg_typeof(_return_type)
       WHEN 'public.result1'::regtype THEN
          RETURN QUERY
          SELECT t.col1, t.col2
          FROM   parameterbased_return_table t;
       WHEN 'public.result2'::regtype THEN
          RETURN QUERY
          SELECT t.col3, t.col4  -- returning actual int values
          FROM   parameterbased_return_table t;
       ELSE
          -- Handle other conditions or return an empty result set
          -- just don't return anything (simpler)
       END CASE;
    END
    $func$;
    

    Call:

    SELECT * FROM public.get_parameterbased_return_table1(null::public.result1);
    
    col1 col2
    A B
    dfdf dfe
    SELECT * FROM public.get_parameterbased_return_table1(null::public.result2);
    
    col3 col4
    11 22
    14 545

    fiddle

    This also returns original data types. You can really return anything here.

    Plain column aliases

    Again, you have to provide the information in the call. Keeping your original function, you can use column aliases in the call:

    SELECT * FROM public.get_parameterbased_return_table('condition2') AS t(col3, col4);
    
    col3 col4
    11 22
    14 545

    fiddle