Sólo con un poco de esfuerzo. Algo como esto:
select listagg((case when running_len < 4000 then oi.impression end), ',') within group (order by oi.line)
from (select oi.*,
sum(length(oi.impression) + 1) over (partition by ?? order by oi.line) as running_len
from order_impression oi
) oi
group by ??;
Esto calcula la longitud de ejecución y solo agrega valores que no superan la longitud. El ??
es lo que sea que esté usando para la agregación. Esto supone que line
es único, por lo que el order by
es estable.
Esto no incluirá la impression
eso excede la longitud, y nada después de eso. No corta la impresión. Esa lógica es posible, pero complica la consulta.