-- RPC reader per pgvector via Supabase/PostgREST (spec D11, ยง5.4). -- Eseguire sul database Supabase centrale come owner dello schema `vectors`. -- Prerequisiti: -- - schema `vectors` con tabelle schema_records/evidence/memory -- - colonne: record_key text unique, kind text, content_hash text, metadata jsonb, -- embedding vector(N), indexed_at timestamptz -- - ruolo/key PostgREST dedicato `vector_reader` (lettura: search_similar, list_tables) -- -- A differenza del writer RPC (che era gia' nel repo), i reader RPC vivevano lato server -- in Supabase e non erano mai stati versionati. Li si riporta qui per completezza del -- modello dual-key: la key reader riceve solo EXECUTE su queste due funzioni, niente -- SELECT raw sulle tabelle. Il cleanup/rebuild resta solo-server (vedi writer RPC). create or replace function public._assert_vector_read_table(table_name text) returns void language plpgsql stable as $$ begin if table_name not in ('schema_records', 'evidence', 'memory') then raise sqlstate 'PGRST' using message = json_build_object('message', 'Unsupported vector table')::text, detail = json_build_object('status', 400)::text; end if; end; $$; create or replace function public.search_similar( table_name text, query_embedding vector, limit_count integer, kinds text[] default null, metadata_filter jsonb default null ) returns table(id bigint, similarity real, metadata jsonb) language plpgsql security definer set search_path = public, vectors, extensions as $$ begin perform public._assert_vector_read_table(table_name); if metadata_filter is not null and ( table_name <> 'evidence' or not (metadata_filter ? 'vector_generation') or not (metadata_filter ? 'document_ids') or not (metadata_filter ? 'workspace_id') or jsonb_object_length(metadata_filter) <> 3 or jsonb_typeof(metadata_filter->'document_ids') <> 'array' ) then raise exception 'invalid Evidence metadata filter'; end if; return query execute format( 'select t.id, (1 - (t.embedding <=> $1))::real as similarity, t.metadata from vectors.%I t where ($3 is null or t.kind = any ($3)) and ($4 is null or ( t.metadata->>''vector_generation'' = $4->>''vector_generation'' and t.metadata->>''workspace_id'' = $4->>''workspace_id'' and t.metadata->>''document_id'' in ( select jsonb_array_elements_text($4->''document_ids'') ) )) order by t.embedding <=> $1 limit $2', table_name ) using query_embedding, limit_count, kinds, metadata_filter; end; $$; create or replace function public.list_tables() returns table(table_name text, vector_dimensions integer, record_count bigint) language plpgsql security definer set search_path = public, vectors, extensions as $$ begin return query select c.relname::text as table_name, -- dimensione ricavata dalla colonna embedding (vector(N)) se presente coalesce( (select (regexp_matches(format_type(a.atttypid, a.atttypmod), 'vector\((\d+)\)'))[1])::int, 0 ) as vector_dimensions, c.reltuples::bigint as record_count from pg_class c join pg_namespace n on n.oid = c.relnamespace left join pg_attribute a on a.attrelid = c.oid and a.attname = 'embedding' where n.nspname = 'vectors' and c.relkind in ('r', 'p') and c.relname in ('schema_records', 'evidence', 'memory') order by c.relname; end; $$; revoke all on function public._assert_vector_read_table(text) from public; revoke all on function public.search_similar(text, vector, integer, text[], jsonb) from public; revoke all on function public.list_tables() from public; -- Revoca dai ruoli client generici, poi abilita solo il reader dedicato -- (e il writer, che usa existing_vector_hashes per l'upsert incrementale). do $$ begin if exists (select 1 from pg_roles where rolname = 'anon') then revoke all on function public.search_similar(text, vector, integer, text[], jsonb) from anon; revoke all on function public.list_tables() from anon; end if; if exists (select 1 from pg_roles where rolname = 'authenticated') then revoke all on function public.search_similar(text, vector, integer, text[], jsonb) from authenticated; revoke all on function public.list_tables() from authenticated; end if; if exists (select 1 from pg_roles where rolname = 'vector_reader') then grant execute on function public.search_similar(text, vector, integer, text[], jsonb) to vector_reader; grant execute on function public.list_tables() to vector_reader; end if; end $$; notify pgrst, 'reload schema';