I work on the following test table:
select * from test;
id | broker | affiliate | leads
----+---------+-----------+-------
1 | broker1 | aff1 | 1
2 | broker1 | aff2 | 2
3 | broker2 | aff2 | 4
(3 rows)
I want to create a function which will accept group by
column name and where
predicate. Here is a working example of the function:
DROP FUNCTION report2(TEXT,TEXT);
CREATE OR REPLACE FUNCTION report2(
group_by_column_name TEXT,
sql_where TEXT
)
RETURNS RECORD
AS $$
DECLARE
query TEXT;
result_row RECORD;
BEGIN
query := 'SELECT ';
IF group_by_column_name <> '' THEN
query := query || group_by_column_name || ', ';
END IF;
query := query || 'sum(leads) FROM test';
IF sql_where <> '' THEN
query := query || ' WHERE ' || sql_where;
END IF;
IF group_by_column_name <> '' THEN
query := query || ' GROUP BY(' || group_by_column_name || ')';
END IF;
RAISE NOTICE 'query: %;', query;
EXECUTE query INTO result_row;
RETURN result_row;
END
$$ LANGUAGE 'plpgsql';
and it's usage:
select report2('broker', '');
NOTICE: query: SELECT broker, sum(leads) FROM test GROUP BY(broker);
report2
-------------
(broker1,3)
(1 row)
I'm trying to get not only a first row of the query but all the rows (obviously).
I tried the following:
DROP FUNCTION report2(TEXT,TEXT);
CREATE OR REPLACE FUNCTION report2(
group_by_column_name TEXT,
sql_where TEXT
)
RETURNS SETOF RECORD
AS $$
DECLARE
query TEXT;
result_row RECORD;
BEGIN
query := 'SELECT ';
IF group_by_column_name <> '' THEN
query := query || group_by_column_name || ', ';
END IF;
query := query || 'sum(leads) FROM test';
IF sql_where <> '' THEN
query := query || ' WHERE ' || sql_where;
END IF;
IF group_by_column_name <> '' THEN
query := query || ' GROUP BY(' || group_by_column_name || ')';
END IF;
RAISE NOTICE 'query: %;', query;
-- Does not worK:
-- FOR result_row IN EXECUTE query
-- LOOP
-- RETURN NEXT result_row;
-- END LOOP;
-- Does not worK:
-- RETURN QUERY EXECUTE query;
END
$$ LANGUAGE 'plpgsql';
In both cases, with FOR
or RETURN QUERY
, the error occurs:
NOTICE: query: SELECT broker, sum(leads) FROM test GROUP BY(broker);
ERROR: materialize mode required, but it is not allowed in this context
CONTEXT: PL/pgSQL function report2(text,text) line 21 at RETURN NEXT
From the [docs][1]:
If multiple rows are returned, only the first will be assigned to the INTO variable(s).
So, how is it possible to get the entire results?
[1]: https://(https://www.postgresql.org/docs/current/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN)
I would return setof json
instead of record
because the number of result rows varies and json
is more usable than record
. So here is the function:
create function report2(arg_group text default '', arg_where text default '')
returns setof json language plpgsql as
$$
begin
return query execute format(
'select to_json(t) from (select %s sum(leads) from test %s %s) t',
case when arg_group = '' then '' else arg_group || ',' end,
case when arg_where = '' then '' else 'where ' || arg_where end,
case when arg_group = '' then '' else 'group by ' || arg_group end
);
end;
$$
select report2('broker');
report2 |
---|
{"broker":"broker1","sum":3} |
{"broker":"broker2","sum":4} |