You can write a function as in PHP. Because of specific pl/pgSQL restrictions the simplest option is to write a function with one text parameter and returning setof record.
create or replace function func (cols text)
returns setof record language plpgsql as $$
begin
return query execute format (
'SELECT %s, MIN(start_date) start_date, MAX(end_date) end_date
FROM (
SELECT %s, start_date, end_date,
MAX(new_start) OVER (
PARTITION BY %s
ORDER BY start_date, end_date
) AS left_edge
FROM (
SELECT %s, start_date, end_date,
CASE WHEN GREATEST(
MIN(start_date) OVER (
PARTITION BY %s
ORDER BY start_date, end_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
),
start_date - INTERVAL ''90 days''
) < (
MAX(end_date) OVER (
PARTITION BY %s
ORDER BY start_date, end_date
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
)
)
THEN NULL
ELSE start_date
END AS new_start
FROM product_activity
) s1
) s2
GROUP BY %s, left_edge',
cols, cols, cols, cols, cols, cols, cols);
end $$;
The only disadvantage of this method is a way in which you call the function - it must be casted to explicit composite type.
select * from func('a1, a2')
as (a1 int, a2 int, start_date date, end_date date);
select * from func('a1, a3, a5')
as (a1 int, a3 int, a5 int, start_date date, end_date date);