-- Ensure storage objects for all artefacts are removed when a file is deleted -- by deleting the entire "cabinet_id/file_id" directory prefix in Storage. -- Helper to delete all objects under a prefix create or replace function public._delete_storage_prefix(p_bucket text, p_prefix text) returns void language plpgsql security definer set search_path to 'public', 'storage' as $$ begin if p_bucket is null or p_prefix is null then return; end if; -- Delete any objects whose name starts with the prefix + '/' delete from storage.objects where bucket_id = p_bucket and name like p_prefix || '/%'; -- In case an object exists exactly at the prefix (rare but safe) delete from storage.objects where bucket_id = p_bucket and name = p_prefix; end $$; -- Update file-level GC to also delete the parent directory prefix (cabinet_id/file_id) create or replace function public._storage_gc_sql() returns trigger language plpgsql security definer set search_path to 'public', 'storage' as $$ declare v_prefix text; begin -- Derive directory prefix from the file path by removing the last segment (filename) -- Example: 'cabinet_id/file_id/filename.ext' -> 'cabinet_id/file_id' v_prefix := regexp_replace(old.path, '/[^/]+$', ''); if tg_op = 'DELETE' then -- Delete the original object and any artefacts under the file's directory perform public._delete_storage_objects(old.bucket, old.path); perform public._delete_storage_prefix(old.bucket, v_prefix); elsif tg_op = 'UPDATE' then if (old.bucket is distinct from new.bucket) or (old.path is distinct from new.path) then perform public._delete_storage_objects(old.bucket, old.path); perform public._delete_storage_prefix(old.bucket, v_prefix); end if; end if; return null; end $$;