Files
Jyotisha/frontend/supabase/migrations/20260925040000_deleted_report_release.sql
T
jesse-uxandClaude Code 4d801e53c3
Independent Staging Quality Gate / validate (push) Successful in 16m28s
Independent Staging Quality Gate / publish (push) Successful in 3m33s
fix: integrate people archive, report reader and western chart corrections
Validate on Linux Node 22 and PostgreSQL 17: 3894 frontend tests and 64 database tests pass, with no removed test names or new failures. Preserve static Home, bounded gzip, assertion-change records and manual acceptance gaps.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-25 20:41:16 +08:00

65 lines
2.9 KiB
PL/PgSQL

begin;
-- A subject deletion cascades through reports/jobs before a worker can observe
-- the missing subject. Release the reservation in that same transaction.
create or replace function public.release_deleted_report_reservation()
returns trigger
language plpgsql
security definer
set search_path = ''
as $$
declare
released record;
begin
-- A ready document has already been delivered, even if the worker is between
-- its durable ready write and billing completion. Do not refund that window.
if old.status = 'ready' then return old; end if;
-- Account removal may already have cascaded away the wallet itself.
if not exists (select 1 from public.profiles where id = old.user_id) then return old; end if;
select * into released from public.release_usage(old.user_id, old.request_id::text, 'report_deleted');
-- Completed usage remains charged. Any other failure must roll back deletion
-- rather than orphan a paid reservation with no durable job left to refund it.
if not released.success and released.error_code is distinct from 'request_completed' then
raise exception 'report_reservation_release_failed';
end if;
return old;
end;
$$;
revoke all on function public.release_deleted_report_reservation() from public, anon, authenticated;
create trigger personal_reports_release_before_delete
before delete on public.personal_reports
for each row execute function public.release_deleted_report_reservation();
-- Match the worker's job -> report lock order before the cascading delete.
-- Definer is needed only for job locks; the caller identity/ownership check is
-- explicit and no arbitrary refund operation is exposed to authenticated.
create or replace function public.delete_chart_subject(p_id uuid)
returns void
language plpgsql
security definer
set search_path = ''
as $$
declare
owner uuid := (select auth.uid());
begin
if owner is null then raise exception 'chart_subject_unauthenticated'; end if;
perform 1 from public.chart_profiles
where id = p_id and user_id = owner and role = 'other' for update;
if not found then raise exception 'chart_subject_not_found'; end if;
perform 1 from public.personal_report_jobs j
join public.personal_reports r on r.user_id = j.user_id and r.request_id = j.request_id
where r.user_id = owner and r.chart_profile_id = p_id
order by j.id for update of j;
perform 1 from public.personal_reports
where user_id = owner and chart_profile_id = p_id order by id for update;
delete from public.chat_sessions where user_id = owner and chart_profile_id = p_id::text;
delete from public.personal_reports where user_id = owner and chart_profile_id = p_id;
delete from public.chart_profiles where id = p_id and user_id = owner and role = 'other';
end;
$$;
revoke all on function public.delete_chart_subject(uuid) from public, anon;
grant execute on function public.delete_chart_subject(uuid) to authenticated;
commit;