create or replace function flatten(p_input jsonb, p_path text)
  returns table(path text, key text, value text)
as
$$
begin
  if jsonb_typeof(p_input) = 'array' then
    return query
      select f.*
      from jsonb_array_elements(p_input) as a(element)
        cross join flatten(a.element, p_path) f;
  else
    return query
      select p_path, e.key, e.value #>> '{}'
      from jsonb_each(p_input) as e(key, value)
      where jsonb_typeof(e.value) not in ('object', 'array')
        or (jsonb_typeof(e.value) = 'array' and jsonb_typeof(e.value -> 0) <> 'object')

      union all

      select f.*
      from jsonb_each(p_input) as t(key,value)
        cross join flatten(t.value, p_path||'/'||t.key) as f
      where jsonb_typeof(t.value) = 'object'

      union all

      select f.*
      from jsonb_each(p_input) as t(key,value)
        cross join jsonb_array_elements(t.value) as a(element)
        cross join flatten(a.element, p_path||'/'||t.key) as f
      where jsonb_typeof(t.value) = 'array'
        and jsonb_typeof(t.value -> 0) = 'object';
  end if;
end;
$$
language plpgsql
immutable
parallel safe
;

create or replace function flatten(p_input jsonb)
  returns table(path text, key text, value text)
as
$$
  select *
  from flatten(p_input, '');
$$
language sql
immutable
parallel safe
;