-- ============================================================================= -- F1.2 — Schema-per-tenant: helpers de resolução de schema -- Adaptação ao modelo multi-membership deste projeto (docs/F0_categorizacao.md D2): -- profiles.tenant_id é NULL; membership vive em tenant_members (multi-tenant). -- Logo NÃO existe current_tenant_schema() — RPCs recebem p_tenant_id explícito -- e validam via tenant_schema_checked(p_tenant_id). -- ============================================================================= BEGIN; -- slug -> nome de schema (validado). Retorna NULL se slug inválido. CREATE OR REPLACE FUNCTION public.tenant_schema_name(p_slug text) RETURNS text LANGUAGE sql IMMUTABLE AS $$ SELECT CASE WHEN p_slug ~ '^[a-z][a-z0-9_]{1,47}$' THEN 'tenant_' || p_slug ELSE NULL END; $$; -- tenant_id -> nome de schema CREATE OR REPLACE FUNCTION public.tenant_schema_for(p_tenant_id uuid) RETURNS text LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public', 'pg_temp' AS $$ SELECT public.tenant_schema_name(t.slug) FROM public.tenants t WHERE t.id = p_tenant_id; $$; -- nome de schema -> tenant_id (CRÍTICO pra triggers: a coluna tenant_id não -- existe mais nas tabelas tenant; o schema é a identidade) CREATE OR REPLACE FUNCTION public.tenant_id_for_schema(p_schema text) RETURNS uuid LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public', 'pg_temp' AS $$ SELECT t.id FROM public.tenants t WHERE public.tenant_schema_name(t.slug) = p_schema; $$; -- Resolve schema de um tenant COM validação de acesso do usuário logado. -- Substitui o current_tenant_schema() do blueprint (que assumia 1 tenant/usuário). CREATE OR REPLACE FUNCTION public.tenant_schema_checked(p_tenant_id uuid) RETURNS text LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public', 'pg_temp' AS $$ DECLARE v_schema text; BEGIN IF p_tenant_id IS NULL THEN RAISE EXCEPTION 'tenant_schema_checked: p_tenant_id obrigatório'; END IF; IF NOT public.is_tenant_member(p_tenant_id) AND NOT public.is_saas_admin() THEN RAISE EXCEPTION 'acesso negado ao tenant %', p_tenant_id USING ERRCODE = '42501'; END IF; v_schema := public.tenant_schema_for(p_tenant_id); IF v_schema IS NULL THEN RAISE EXCEPTION 'tenant % não encontrado ou slug inválido', p_tenant_id; END IF; RETURN v_schema; END; $$; COMMIT;