geocrop-platform./apps/nextgen/sql/supabase_tenant_reconcile.sql

201 lines
12 KiB
SQL

-- ============================================================================
-- Africa Alert — Supabase tenant_id reconciliation script
-- Paste into Supabase SQL Editor, then RUN.
-- Safe to re-run (idempotent).
-- Tenant UUID hard-coded below (matches local system_settings.tenant_id).
-- ============================================================================
DO $script$
DECLARE
v_tenant_id uuid := '5b39bcb5-506a-456d-b7cb-91730b397cc1';
BEGIN
-- Sanity check: tenant must exist in the tenants table.
IF NOT EXISTS (SELECT 1 FROM public.tenants WHERE id = v_tenant_id) THEN
RAISE EXCEPTION 'Tenant % not found in public.tenants. Insert it first or re-run the registration cycle on the server.', v_tenant_id;
END IF;
---------------------------------------------------------------------------
-- 1. Backfill null tenant_id on every business table that carries one
-- (skip 'tenants' itself, plus 'notices' / 'sync_logs' / 'sync_metadata'
-- / 'sync_config' which don't have a tenant_id column).
---------------------------------------------------------------------------
UPDATE public.admission_enquiries SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.applicants SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.assignments SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.attendance SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.attendance_new SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.calendar_events SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.classes SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.club_announcements SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.club_attendance SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.club_chats SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.club_memberships SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.clubs SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.complaints SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.course_enrolments SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.course_grades SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.courses SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.crossword_clues SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.crossword_games SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.crossword_sessions SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.departments SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.enrollments SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.events SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.exam_answers SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.exam_attempts SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.exam_groups SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.exam_schedules SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.expenses SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.fee_groups SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.grades SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.hostel_attendance SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.hostel_fees SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.hostels SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.item_categories SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.item_issues SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.item_stock SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.items SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.leave_requests SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.leave_types SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.library_books SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.library_fines SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.library_issues SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.library_reservations SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.messages SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.messages_new SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.parent_students SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.payments SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.payroll_runs SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.payslips SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.phone_call_logs SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.pickup_points SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.postal_dispatch SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.question_banks SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.room_assignments SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.room_types SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.rooms SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.routes SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.salary_grades SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.settings SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.staff_attendance SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.staff_records SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.staff_roles SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.stock_transactions SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.store_locations SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.student_fees SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.subjects SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.subjects_new SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.submissions SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.suppliers SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.system_settings SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.transport_allocations SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.transport_attendance SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.users SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.vacancies SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.vehicle_routes SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.vehicles SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.visitor_logs SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
UPDATE public.audit_logs SET tenant_id = v_tenant_id WHERE tenant_id IS NULL;
---------------------------------------------------------------------------
-- 2. Enforce NOT NULL now that all rows have a tenant.
-- Wrapped in dynamic SQL + DO blocks so missing columns don't blow up.
---------------------------------------------------------------------------
PERFORM (
SELECT string_agg(
format('ALTER TABLE public.%I ALTER COLUMN tenant_id SET NOT NULL', t.table_name),
'; '
)
FROM information_schema.columns
WHERE table_schema = 'public'
AND column_name = 'tenant_id'
AND is_nullable = 'YES'
AND table_name <> 'tenants'
)::text;
IF (SELECT string_agg(
format('ALTER TABLE public.%I ALTER COLUMN tenant_id SET NOT NULL', t.table_name), '; ')
FROM information_schema.columns
WHERE table_schema='public' AND column_name='tenant_id'
AND is_nullable='YES' AND table_name <> 'tenants') IS NOT NULL THEN
EXECUTE (
SELECT string_agg(
format('ALTER TABLE public.%I ALTER COLUMN tenant_id SET NOT NULL', t.table_name),
'; '
)
FROM information_schema.columns
WHERE table_schema = 'public'
AND column_name = 'tenant_id'
AND is_nullable = 'YES'
AND table_name <> 'tenants'
);
END IF;
---------------------------------------------------------------------------
-- 3. Indexes on tenant_id (RLS checks every row; you want these).
---------------------------------------------------------------------------
PERFORM (
SELECT string_agg(
format('CREATE INDEX IF NOT EXISTS %I ON public.%I (tenant_id)', t.table_name || '_tenant_id_idx', t.table_name),
'; '
)
FROM information_schema.columns
WHERE table_schema = 'public'
AND column_name = 'tenant_id'
AND table_name <> 'tenants'
);
IF (SELECT string_agg(
format('CREATE INDEX IF NOT EXISTS %I ON public.%I (tenant_id)', t.table_name || '_tenant_id_idx', t.table_name), '; ')
FROM information_schema.columns
WHERE table_schema='public' AND column_name='tenant_id'
AND table_name <> 'tenants') IS NOT NULL THEN
EXECUTE (
SELECT string_agg(
format('CREATE INDEX IF NOT EXISTS %I ON public.%I (tenant_id)', t.table_name || '_tenant_id_idx', t.table_name),
'; '
)
FROM information_schema.columns
WHERE table_schema = 'public'
AND column_name = 'tenant_id'
AND table_name <> 'tenants'
);
END IF;
RAISE NOTICE 'Tenant % reconciled: rows backfilled, NOT NULL enforced, indexes created.', v_tenant_id;
END
$script$;
-- ============================================================================
-- 4. Enable RLS + tenant-isolation policies on every tenant_id-bearing table.
-- service_role key bypasses RLS, so the sync engine still has full access.
-- Run as a separate batch (RLS changes can't be inside the DO block).
-- ============================================================================
DO $rls$
DECLARE
r record;
BEGIN
FOR r IN
SELECT c.table_name
FROM information_schema.columns c
WHERE c.table_schema = 'public'
AND c.column_name = 'tenant_id'
AND c.table_name <> 'tenants'
LOOP
EXECUTE format('ALTER TABLE public.%I ENABLE ROW LEVEL SECURITY', r.table_name);
-- Drop any prior version of our policy to keep this idempotent.
EXECUTE format('DROP POLICY IF EXISTS tenant_isolation ON public.%I', r.table_name);
EXECUTE format(
'CREATE POLICY tenant_isolation ON public.%I
USING (tenant_id::text = current_setting(''app.current_tenant'', true))
WITH CHECK (tenant_id::text = current_setting(''app.current_tenant'', true))',
r.table_name
);
END LOOP;
END
$rls$;