You cannot return rows with different data types from plpgsql function in the sense that all returned values in one call must be of the same type.
If you want to return rows with variable number of columns you should declare record or setof record as return type of the function.
In this case however you have to specify expected row structure in the function call.
Functions returning one row should have record as returned type. Example:
create or replace function test_record(number_of_columns int)
returns record language plpgsql as $$
begin
case number_of_columns
when 1 then
return row('a'::text);
when 2 then
return row('a'::text, 2::integer);
else
return row('a'::text, 2::integer, 5.5::numeric);
end case;
end $$;
When executing a query you have to know how many columns the function returns:
select * from test_record(1) as (column1 text);
column1
---------
a
select * from test_record(2) as (column1 text, column2 integer);
column1 | column2
---------+---------
a | 2
select * from test_record(3) as (column1 text, column2 integer, column3 numeric);
column1 | column2 | column3
---------+---------+---------
a | 2 | 5.5
-- but:
select * from test_record(3) as (column1 text);
ERROR: returned record type does not match expected record type
DETAIL: Number of returned columns (3) does not match expected column count (1).
CONTEXT: PL/pgSQL function test_record(integer) while casting return value to function's return type
Functions returning more than one row should have setof record as returned type:
create or replace function test_set_of_record(number_of_columns int)
returns setof record language plpgsql as $$
begin
case number_of_columns
when 1 then
return query select i::text from generate_series(1, 3) i;
when 2 then
return query select i::text, i from generate_series(1, 3) i;
else
return query select i::text, i, i::numeric from generate_series(1, 3) i;
end case;
end $$;
select * from test_set_of_record(3) as (column1 text, column2 integer, column3 numeric);
column1 | column2 | column3
---------+---------+---------
1 | 1 | 1
2 | 2 | 2
3 | 3 | 3
(3 rows)
Read more: 7.2.1.4. Table Functions.
Another option is to use null values to fit a row to returned type, example:
create or replace function test_with_nulls()
returns table (col1 text, col2 int, col3 numeric)
language plpgsql as $$
begin
return query select 'one'::text, null::int, null::numeric;
return query select 'two'::text, 2::int, null::numeric;
return query select 'three'::text, 3::int, 3.3::numeric;
end $$;
select * from test_with_nulls();
col1 | col2 | col3
-------+------+------
one | |
two | 2 |
three | 3 | 3.3
(3 rows)