-- ============================================================================= -- Fix (regressão schema-per-tenant): log_audit_change quebra INSERT em tabelas -- GLOBAIS auditadas. -- -- log_audit_change deriva o tenant via tenant_id_for_schema(TG_TABLE_SCHEMA). -- Para tabelas em tenant_ isso resolve certo. Mas o trigger também está -- em public.tenant_members (tabela global) — e tenant_id_for_schema('public') -- retorna NULL, violando audit_logs.tenant_id (NOT NULL). Resultado: QUALQUER -- INSERT em tenant_members falhava (provisionamento, aceite de convite). -- -- Fix: quando o schema não resolve um tenant (tabela global), usa o tenant_id -- da própria linha (tenant_members.tenant_id). Se ainda assim for NULL, não -- audita — mas NUNCA quebra a operação de negócio. -- ============================================================================= CREATE OR REPLACE FUNCTION public.log_audit_change() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp' AS $function$ DECLARE v_tenant_id uuid; v_entity_id text; v_old jsonb; v_new jsonb; v_changed text[]; v_heavy text[] := ARRAY['content','content_html','content_json','raw_data','signature_data','pdf_blob','binary','body_html','body_text']; v_noise text[] := ARRAY['updated_at','last_seen_at','last_activity_at']; BEGIN v_tenant_id := public.tenant_id_for_schema(TG_TABLE_SCHEMA); -- tabela global (public.*): cai no tenant_id da própria linha, se existir IF v_tenant_id IS NULL THEN v_tenant_id := NULLIF(to_jsonb(COALESCE(NEW, OLD)) ->> 'tenant_id', '')::uuid; END IF; -- sem tenant resolvível → não audita, mas não quebra a operação IF v_tenant_id IS NULL THEN RETURN COALESCE(NEW, OLD); END IF; IF TG_OP = 'DELETE' THEN v_entity_id := OLD.id::text; v_old := to_jsonb(OLD) - v_heavy; v_new := NULL; ELSIF TG_OP = 'INSERT' THEN v_entity_id := NEW.id::text; v_old := NULL; v_new := to_jsonb(NEW) - v_heavy; ELSE v_entity_id := NEW.id::text; v_old := to_jsonb(OLD) - v_heavy; v_new := to_jsonb(NEW) - v_heavy; SELECT array_agg(key ORDER BY key) INTO v_changed FROM jsonb_each(to_jsonb(NEW)) AS kv(key, value) WHERE (to_jsonb(OLD))->kv.key IS DISTINCT FROM kv.value; IF v_changed IS NULL THEN RETURN NEW; END IF; IF v_changed <@ v_noise THEN RETURN NEW; END IF; END IF; INSERT INTO public.audit_logs (tenant_id, user_id, entity_type, entity_id, action, old_values, new_values, changed_fields) VALUES (v_tenant_id, auth.uid(), TG_TABLE_NAME, v_entity_id, lower(TG_OP), v_old, v_new, v_changed); RETURN COALESCE(NEW, OLD); END $function$;