\set ON_ERROR_STOP on
begin transaction isolation level repeatable read read only;
\if :{?snapshot_id}
set transaction snapshot :'snapshot_id';
\endif
set local statement_timeout = '30s';

select jsonb_build_object(
  'kind', 'identity',
  'database', current_database(),
  'current_user', current_user,
  'server_version_num', current_setting('server_version_num')::integer,
  'server_encoding', current_setting('server_encoding'),
  'database_collation', (select datcollate from pg_database where datname = current_database()),
  'database_ctype', (select datctype from pg_database where datname = current_database()),
  'transaction_read_only', current_setting('transaction_read_only'),
  'server_address', host(inet_server_addr()),
  'server_port', inet_server_port(),
  'ssl', coalesce((select ssl from pg_stat_ssl where pid = pg_backend_pid()), false),
  'ssl_version', (select version from pg_stat_ssl where pid = pg_backend_pid()),
  'captured_at', clock_timestamp()
)::text;

select jsonb_build_object(
  'kind', 'schemas',
  'items', coalesce(
    (select jsonb_agg(nspname order by nspname)
       from pg_namespace
      where nspname in ('public', 'kb_stage')),
    '[]'::jsonb
  )
)::text;

select jsonb_build_object(
  'kind', 'extensions',
  'items', coalesce(
    (select jsonb_agg(jsonb_build_object('name', extname, 'version', extversion) order by extname)
       from pg_extension),
    '[]'::jsonb
  )
)::text;

select jsonb_build_object(
  'kind', 'application_roles',
  'items', coalesce(
    (select jsonb_agg(
       jsonb_build_object(
         'name', role_row.rolname,
         'can_login', role_row.rolcanlogin,
         'superuser', role_row.rolsuper,
         'create_db', role_row.rolcreatedb,
         'create_role', role_row.rolcreaterole,
         'inherit', role_row.rolinherit,
         'replication', role_row.rolreplication,
         'bypass_rls', role_row.rolbypassrls,
         'connection_limit', role_row.rolconnlimit,
         'default_transaction_read_only', (
           select split_part(setting, '=', 2)
             from unnest(coalesce(role_row.rolconfig, array[]::text[])) setting
            where split_part(setting, '=', 1) = 'default_transaction_read_only'
            limit 1
         ),
         'settings', (
           select coalesce(
             jsonb_agg(
               jsonb_build_object(
                 'name', split_part(setting, '=', 1),
                 'value', substring(setting from position('=' in setting) + 1)
               ) order by split_part(setting, '=', 1), setting
             ),
             '[]'::jsonb
           )
           from unnest(coalesce(role_row.rolconfig, array[]::text[])) setting
         ),
         'database_settings', (
           select coalesce(
             jsonb_agg(
               jsonb_build_object(
                 'name', split_part(setting, '=', 1),
                 'value', substring(setting from position('=' in setting) + 1)
               ) order by split_part(setting, '=', 1), setting
             ),
             '[]'::jsonb
           )
           from pg_db_role_setting database_setting
           cross join lateral unnest(coalesce(database_setting.setconfig, array[]::text[])) setting
           where database_setting.setrole = role_row.oid
             and database_setting.setdatabase = (
               select database_row.oid
               from pg_database database_row
               where database_row.datname = current_database()
             )
         )
       ) order by role_row.rolname)
       from pg_roles role_row
      where role_row.rolname in ('kb_gate_owner', 'kb_apply', 'kb_review', 'kb_observatory_read')),
    '[]'::jsonb
  )
)::text;

select jsonb_build_object(
  'kind', 'role_memberships',
  'items', coalesce(
    (select jsonb_agg(
       jsonb_build_object(
         'role', role_row.rolname,
         'member', member_row.rolname,
         'grantor', grantor_row.rolname,
         'admin_option', membership.admin_option,
         'inherit_option', membership.inherit_option,
         'set_option', membership.set_option
       ) order by role_row.rolname, member_row.rolname, grantor_row.rolname)
       from pg_auth_members membership
       join pg_roles role_row on role_row.oid = membership.roleid
       join pg_roles member_row on member_row.oid = membership.member
       join pg_roles grantor_row on grantor_row.oid = membership.grantor
      where role_row.rolname in ('kb_gate_owner', 'kb_apply', 'kb_review', 'kb_observatory_read')
         or member_row.rolname in ('kb_gate_owner', 'kb_apply', 'kb_review', 'kb_observatory_read')),
    '[]'::jsonb
  )
)::text;

with ownership as (
  select
    'database:canonical_database'::text as sort_key,
    'database'::text as object_type,
    null::text as schema,
    'canonical_database'::text as name,
    null::text as identity_arguments,
    pg_get_userbyid(database_row.datdba) as owner
  from pg_database database_row
  where database_row.datname = current_database()

  union all

  select
    format('schema:%s', namespace.nspname),
    'schema',
    namespace.nspname,
    namespace.nspname,
    null::text,
    pg_get_userbyid(namespace.nspowner)
  from pg_namespace namespace
  where namespace.nspname in ('public', 'kb_stage')

  union all

  select
    format('relation:%s.%s', namespace.nspname, relation.relname),
    case relation.relkind
      when 'r' then 'table'
      when 'p' then 'partitioned_table'
      when 'v' then 'view'
      when 'm' then 'materialized_view'
      when 'S' then 'sequence'
      when 'f' then 'foreign_table'
    end,
    namespace.nspname,
    relation.relname,
    null::text,
    pg_get_userbyid(relation.relowner)
  from pg_class relation
  join pg_namespace namespace on namespace.oid = relation.relnamespace
  where namespace.nspname in ('public', 'kb_stage')
    and relation.relkind in ('r', 'p', 'v', 'm', 'S', 'f')

  union all

  select
    format(
      'function:%s.%s(%s)',
      namespace.nspname,
      procedure.proname,
      pg_get_function_identity_arguments(procedure.oid)
    ),
    'function',
    namespace.nspname,
    procedure.proname,
    pg_get_function_identity_arguments(procedure.oid),
    pg_get_userbyid(procedure.proowner)
  from pg_proc procedure
  join pg_namespace namespace on namespace.oid = procedure.pronamespace
  where namespace.nspname in ('public', 'kb_stage')

  union all

  select
    format('type:%s.%s', namespace.nspname, type_row.typname),
    case type_row.typtype when 'd' then 'domain' else 'enum' end,
    namespace.nspname,
    type_row.typname,
    null::text,
    pg_get_userbyid(type_row.typowner)
  from pg_type type_row
  join pg_namespace namespace on namespace.oid = type_row.typnamespace
  where namespace.nspname in ('public', 'kb_stage')
    and type_row.typtype in ('d', 'e')
)
select jsonb_build_object(
  'kind', 'object_ownership',
  'items', coalesce(
    jsonb_agg(to_jsonb(ownership) - 'sort_key' order by sort_key),
    '[]'::jsonb
  )
)::text
from ownership;

with acl_rows as (
  select
    format(
      'database:canonical_database:%s:%s:%s',
      acl.grantee,
      acl.privilege_type,
      acl.is_grantable
    ) as sort_key,
    'database'::text as object_type,
    null::text as schema,
    'canonical_database'::text as name,
    null::text as column_name,
    null::text as identity_arguments,
    pg_get_userbyid(acl.grantor) as grantor,
    case when acl.grantee = 0 then 'PUBLIC' else pg_get_userbyid(acl.grantee) end as grantee,
    acl.privilege_type,
    acl.is_grantable
  from pg_database database_row
  cross join lateral aclexplode(coalesce(database_row.datacl, acldefault('d', database_row.datdba))) acl
  where database_row.datname = current_database()

  union all

  select
    format(
      'schema:%s:%s:%s:%s',
      namespace.nspname,
      acl.grantee,
      acl.privilege_type,
      acl.is_grantable
    ),
    'schema',
    namespace.nspname,
    namespace.nspname,
    null::text,
    null::text,
    pg_get_userbyid(acl.grantor),
    case when acl.grantee = 0 then 'PUBLIC' else pg_get_userbyid(acl.grantee) end,
    acl.privilege_type,
    acl.is_grantable
  from pg_namespace namespace
  cross join lateral aclexplode(coalesce(namespace.nspacl, acldefault('n', namespace.nspowner))) acl
  where namespace.nspname in ('public', 'kb_stage')

  union all

  select
    format(
      'relation:%s.%s:%s:%s:%s',
      namespace.nspname,
      relation.relname,
      acl.grantee,
      acl.privilege_type,
      acl.is_grantable
    ),
    case when relation.relkind = 'S' then 'sequence' else 'relation' end,
    namespace.nspname,
    relation.relname,
    null::text,
    null::text,
    pg_get_userbyid(acl.grantor),
    case when acl.grantee = 0 then 'PUBLIC' else pg_get_userbyid(acl.grantee) end,
    acl.privilege_type,
    acl.is_grantable
  from pg_class relation
  join pg_namespace namespace on namespace.oid = relation.relnamespace
  cross join lateral aclexplode(
    coalesce(
      relation.relacl,
      acldefault(case when relation.relkind = 'S' then 'S'::"char" else 'r'::"char" end, relation.relowner)
    )
  ) acl
  where namespace.nspname in ('public', 'kb_stage')
    and relation.relkind in ('r', 'p', 'v', 'm', 'S', 'f')

  union all

  select
    format(
      'column:%s.%s.%s:%s:%s:%s',
      namespace.nspname,
      relation.relname,
      attribute.attname,
      acl.grantee,
      acl.privilege_type,
      acl.is_grantable
    ),
    'column',
    namespace.nspname,
    relation.relname,
    attribute.attname,
    null::text,
    pg_get_userbyid(acl.grantor),
    case when acl.grantee = 0 then 'PUBLIC' else pg_get_userbyid(acl.grantee) end,
    acl.privilege_type,
    acl.is_grantable
  from pg_attribute attribute
  join pg_class relation on relation.oid = attribute.attrelid
  join pg_namespace namespace on namespace.oid = relation.relnamespace
  cross join lateral aclexplode(attribute.attacl) acl
  where namespace.nspname in ('public', 'kb_stage')
    and relation.relkind in ('r', 'p', 'v', 'm', 'f')
    and attribute.attnum > 0
    and not attribute.attisdropped

  union all

  select
    format(
      'function:%s.%s(%s):%s:%s:%s',
      namespace.nspname,
      procedure.proname,
      pg_get_function_identity_arguments(procedure.oid),
      acl.grantee,
      acl.privilege_type,
      acl.is_grantable
    ),
    'function',
    namespace.nspname,
    procedure.proname,
    null::text,
    pg_get_function_identity_arguments(procedure.oid),
    pg_get_userbyid(acl.grantor),
    case when acl.grantee = 0 then 'PUBLIC' else pg_get_userbyid(acl.grantee) end,
    acl.privilege_type,
    acl.is_grantable
  from pg_proc procedure
  join pg_namespace namespace on namespace.oid = procedure.pronamespace
  cross join lateral aclexplode(coalesce(procedure.proacl, acldefault('f', procedure.proowner))) acl
  where namespace.nspname in ('public', 'kb_stage')

  union all

  select
    format(
      'type:%s.%s:%s:%s:%s',
      namespace.nspname,
      type_row.typname,
      acl.grantee,
      acl.privilege_type,
      acl.is_grantable
    ),
    'type',
    namespace.nspname,
    type_row.typname,
    null::text,
    null::text,
    pg_get_userbyid(acl.grantor),
    case when acl.grantee = 0 then 'PUBLIC' else pg_get_userbyid(acl.grantee) end,
    acl.privilege_type,
    acl.is_grantable
  from pg_type type_row
  join pg_namespace namespace on namespace.oid = type_row.typnamespace
  cross join lateral aclexplode(coalesce(type_row.typacl, acldefault('T', type_row.typowner))) acl
  where namespace.nspname in ('public', 'kb_stage')
    and type_row.typtype in ('d', 'e')
)
select jsonb_build_object(
  'kind', 'object_acl',
  'items', coalesce(
    jsonb_agg(
      to_jsonb(acl_rows) - 'sort_key'
      order by
        object_type,
        schema nulls first,
        name,
        column_name nulls first,
        identity_arguments nulls first,
        grantor,
        grantee,
        privilege_type,
        is_grantable
    ),
    '[]'::jsonb
  )
)::text
from acl_rows;

select jsonb_build_object(
  'kind', 'columns',
  'items', coalesce(
    jsonb_agg(
      jsonb_build_object(
        'schema', table_schema,
        'table', table_name,
        'position', ordinal_position,
        'name', column_name,
        'data_type', data_type,
        'udt_name', udt_name,
        'nullable', is_nullable,
        'default', column_default
      ) order by table_schema, table_name, ordinal_position
    ),
    '[]'::jsonb
  )
)::text
from information_schema.columns
where table_schema in ('public', 'kb_stage');

select jsonb_build_object(
  'kind', 'constraints',
  'items', coalesce(
    jsonb_agg(
      jsonb_build_object(
        'schema', namespace.nspname,
        'table', relation.relname,
        'name', constraint_row.conname,
        'type', constraint_row.contype,
        'definition', pg_get_constraintdef(constraint_row.oid, true),
        'validated', constraint_row.convalidated,
        'deferrable', constraint_row.condeferrable,
        'initially_deferred', constraint_row.condeferred
      ) order by namespace.nspname, relation.relname, constraint_row.conname
    ),
    '[]'::jsonb
  )
)::text
from pg_constraint constraint_row
join pg_class relation on relation.oid = constraint_row.conrelid
join pg_namespace namespace on namespace.oid = relation.relnamespace
where namespace.nspname in ('public', 'kb_stage');

select jsonb_build_object(
  'kind', 'indexes',
  'items', coalesce(
    jsonb_agg(
      jsonb_build_object(
        'schema', schemaname,
        'table', tablename,
        'name', indexname,
        'definition', indexdef
      ) order by schemaname, tablename, indexname
    ),
    '[]'::jsonb
  )
)::text
from pg_indexes
where schemaname in ('public', 'kb_stage');

select jsonb_build_object(
  'kind', 'sequences',
  'items', coalesce(
    jsonb_agg(
      jsonb_build_object(
        'schema', schemaname,
        'name', sequencename,
        'data_type', data_type,
        'start_value', start_value,
        'min_value', min_value,
        'max_value', max_value,
        'increment_by', increment_by,
        'cycle', cycle,
        'cache_size', cache_size,
        'last_value', last_value
      ) order by schemaname, sequencename
    ),
    '[]'::jsonb
  )
)::text
from pg_sequences
where schemaname in ('public', 'kb_stage');

select jsonb_build_object(
  'kind', 'views',
  'items', coalesce(
    jsonb_agg(
      jsonb_build_object(
        'schema', schemaname,
        'name', viewname,
        'definition', definition
      ) order by schemaname, viewname
    ),
    '[]'::jsonb
  )
)::text
from pg_views
where schemaname in ('public', 'kb_stage');

select jsonb_build_object(
  'kind', 'functions',
  'items', coalesce(
    jsonb_agg(
      jsonb_build_object(
        'schema', namespace.nspname,
        'name', procedure.proname,
        'identity_arguments', pg_get_function_identity_arguments(procedure.oid),
        'result', pg_get_function_result(procedure.oid),
        'language', language.lanname,
        'volatility', procedure.provolatile,
        'security_definer', procedure.prosecdef,
        'definition', pg_get_functiondef(procedure.oid)
      ) order by namespace.nspname, procedure.proname, pg_get_function_identity_arguments(procedure.oid)
    ),
    '[]'::jsonb
  )
)::text
from pg_proc procedure
join pg_namespace namespace on namespace.oid = procedure.pronamespace
join pg_language language on language.oid = procedure.prolang
where namespace.nspname in ('public', 'kb_stage');

select jsonb_build_object(
  'kind', 'triggers',
  'items', coalesce(
    jsonb_agg(
      jsonb_build_object(
        'schema', namespace.nspname,
        'table', relation.relname,
        'name', trigger_row.tgname,
        'enabled', trigger_row.tgenabled,
        'definition', pg_get_triggerdef(trigger_row.oid, true)
      ) order by namespace.nspname, relation.relname, trigger_row.tgname
    ),
    '[]'::jsonb
  )
)::text
from pg_trigger trigger_row
join pg_class relation on relation.oid = trigger_row.tgrelid
join pg_namespace namespace on namespace.oid = relation.relnamespace
where namespace.nspname in ('public', 'kb_stage')
  and not trigger_row.tgisinternal;

select jsonb_build_object(
  'kind', 'types',
  'items', coalesce(
    jsonb_agg(
      jsonb_build_object(
        'schema', namespace.nspname,
        'name', type_row.typname,
        'kind', type_row.typtype,
        'category', type_row.typcategory,
        'base_type', case when type_row.typbasetype = 0 then null else format_type(type_row.typbasetype, null) end,
        'not_null', type_row.typnotnull,
        'default', type_row.typdefault,
        'enum_values', (
          select coalesce(jsonb_agg(enum_row.enumlabel order by enum_row.enumsortorder), '[]'::jsonb)
          from pg_enum enum_row
          where enum_row.enumtypid = type_row.oid
        )
      ) order by namespace.nspname, type_row.typname
    ),
    '[]'::jsonb
  )
)::text
from pg_type type_row
join pg_namespace namespace on namespace.oid = type_row.typnamespace
where namespace.nspname in ('public', 'kb_stage')
  and type_row.typtype in ('d', 'e');

select jsonb_build_object(
  'kind', 'policies',
  'items', coalesce(
    jsonb_agg(
      jsonb_build_object(
        'schema', schemaname,
        'table', tablename,
        'name', policyname,
        'permissive', permissive,
        'roles', roles,
        'command', cmd,
        'using', qual,
        'check', with_check
      ) order by schemaname, tablename, policyname
    ),
    '[]'::jsonb
  )
)::text
from pg_policies
where schemaname in ('public', 'kb_stage');

select format(
  $manifest$
  select jsonb_build_object(
    'kind', 'table',
    'schema', %L,
    'table', %L,
    'row_count', count(*)::bigint,
    'rowset_md5', md5(coalesce(string_agg(to_jsonb(row_value)::text, E'\n'
      order by (to_jsonb(row_value)::text) collate "C"), ''))
  )::text
  from %I.%I row_value;
  $manifest$,
  schemaname,
  tablename,
  schemaname,
  tablename
)
from pg_tables
where schemaname in ('public', 'kb_stage')
order by schemaname, tablename
\gexec

with started as materialized (
  select clock_timestamp() as at
), observed as materialized (
  select count(*)::bigint as row_count, min(started.at) as started_at
  from public.claims cross join started
), finished as materialized (
  select clock_timestamp() as finished_at, max(row_count) as row_count, min(started_at) as started_at
  from observed
)
select jsonb_build_object(
  'kind', 'performance',
  'query', 'count_claims',
  'elapsed_ms', extract(epoch from (finished_at - started_at)) * 1000,
  'observed_rows', row_count
)::text
from finished;

with started as materialized (
  select clock_timestamp() as at
), candidate as materialized (
  select id from public.claims order by id limit 1
), observed as materialized (
  select count(*)::bigint as row_count, min(started.at) as started_at
  from public.claim_evidence evidence
  join candidate on candidate.id = evidence.claim_id
  cross join started
), finished as materialized (
  select clock_timestamp() as finished_at, max(row_count) as row_count, min(started_at) as started_at
  from observed
)
select jsonb_build_object(
  'kind', 'performance',
  'query', 'claim_evidence_by_claim_id',
  'elapsed_ms', extract(epoch from (finished_at - started_at)) * 1000,
  'observed_rows', row_count
)::text
from finished;

with started as materialized (
  select clock_timestamp() as at
), candidate as materialized (
  select id from public.claims order by id limit 1
), observed as materialized (
  select count(*)::bigint as row_count, min(started.at) as started_at
  from public.claim_edges edge_row
  join candidate on candidate.id in (edge_row.from_claim, edge_row.to_claim)
  cross join started
), finished as materialized (
  select clock_timestamp() as finished_at, max(row_count) as row_count, min(started_at) as started_at
  from observed
)
select jsonb_build_object(
  'kind', 'performance',
  'query', 'claim_edges_by_claim_id',
  'elapsed_ms', extract(epoch from (finished_at - started_at)) * 1000,
  'observed_rows', row_count
)::text
from finished;

with started as materialized (
  select clock_timestamp() as at
), observed as materialized (
  select count(*)::bigint as row_count, min(started.at) as started_at
  from (
    select id from kb_stage.kb_proposals
    where status = 'pending_review'
    order by created_at desc
    limit 25
  ) proposal_row
  cross join started
), finished as materialized (
  select clock_timestamp() as finished_at, max(row_count) as row_count, min(started_at) as started_at
  from observed
)
select jsonb_build_object(
  'kind', 'performance',
  'query', 'pending_proposals_latest',
  'elapsed_ms', extract(epoch from (finished_at - started_at)) * 1000,
  'observed_rows', row_count
)::text
from finished;

rollback;
