From 6011888e91ea5decd5a2f997545be1ad8bdbc415 Mon Sep 17 00:00:00 2001 From: Eric Gullickson <16152721+ericgullickson@users.noreply.github.com> Date: Mon, 16 Feb 2026 09:33:14 -0600 Subject: [PATCH] chore: add UUID identity migration SQL (refs #211) Multi-phase SQL migration converting all user_id columns from VARCHAR(255) auth0_sub to UUID referencing user_profiles.id. Restructures admin_users with UUID PK and user_profile_id FK. Co-Authored-By: Claude Opus 4.6 --- backend/src/_system/migrations/run-all.ts | 1 + .../001_migrate_user_id_to_uuid.sql | 382 ++++++++++++++++++ 2 files changed, 383 insertions(+) create mode 100644 backend/src/core/identity-migration/migrations/001_migrate_user_id_to_uuid.sql diff --git a/backend/src/_system/migrations/run-all.ts b/backend/src/_system/migrations/run-all.ts index 2afd27c..cb573b0 100644 --- a/backend/src/_system/migrations/run-all.ts +++ b/backend/src/_system/migrations/run-all.ts @@ -31,6 +31,7 @@ const MIGRATION_ORDER = [ 'features/audit-log', // Centralized audit logging; independent 'features/ownership-costs', // Depends on vehicles and documents; TCO recurring costs 'features/subscriptions', // Stripe subscriptions; depends on user-profile, vehicles + 'core/identity-migration', // Cross-cutting UUID migration; must run after all feature tables exist ]; // Base directory where migrations are copied inside the image (set by Dockerfile) diff --git a/backend/src/core/identity-migration/migrations/001_migrate_user_id_to_uuid.sql b/backend/src/core/identity-migration/migrations/001_migrate_user_id_to_uuid.sql new file mode 100644 index 0000000..8500037 --- /dev/null +++ b/backend/src/core/identity-migration/migrations/001_migrate_user_id_to_uuid.sql @@ -0,0 +1,382 @@ +-- Migration: 001_migrate_user_id_to_uuid.sql +-- Feature: identity-migration (cross-cutting) +-- Description: Migrate all user identity columns from VARCHAR(255) storing auth0_sub +-- to UUID referencing user_profiles.id. Admin tables restructured with UUID PKs. +-- Requires: All feature tables must exist (runs last in MIGRATION_ORDER) + +BEGIN; + +-- ============================================================================ +-- PHASE 1: Add new UUID columns alongside existing VARCHAR columns +-- ============================================================================ + +-- 1a. Feature tables (17 tables with user_id VARCHAR) +ALTER TABLE vehicles ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE fuel_logs ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE maintenance_records ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE maintenance_schedules ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE documents ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE notification_logs ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE user_notifications ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE user_preferences ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE saved_stations ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE audit_logs ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE ownership_costs ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE email_ingestion_queue ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE pending_vehicle_associations ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE subscriptions ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE donations ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE tier_vehicle_selections ADD COLUMN IF NOT EXISTS user_profile_id UUID; +ALTER TABLE terms_agreements ADD COLUMN IF NOT EXISTS user_profile_id UUID; + +-- 1b. Special user-reference columns (submitted_by/reported_by store auth0_sub) +ALTER TABLE community_stations ADD COLUMN IF NOT EXISTS submitted_by_uuid UUID; +ALTER TABLE station_removal_reports ADD COLUMN IF NOT EXISTS reported_by_uuid UUID; + +-- 1c. Admin table: add id UUID and user_profile_id UUID +ALTER TABLE admin_users ADD COLUMN IF NOT EXISTS id UUID; +ALTER TABLE admin_users ADD COLUMN IF NOT EXISTS user_profile_id UUID; + +-- 1d. Admin-referencing columns: add UUID equivalents +ALTER TABLE admin_audit_logs ADD COLUMN IF NOT EXISTS actor_admin_uuid UUID; +ALTER TABLE admin_audit_logs ADD COLUMN IF NOT EXISTS target_admin_uuid UUID; +ALTER TABLE admin_users ADD COLUMN IF NOT EXISTS created_by_uuid UUID; +ALTER TABLE community_stations ADD COLUMN IF NOT EXISTS reviewed_by_uuid UUID; +ALTER TABLE backup_history ADD COLUMN IF NOT EXISTS created_by_uuid UUID; +ALTER TABLE platform_change_log ADD COLUMN IF NOT EXISTS changed_by_uuid UUID; +ALTER TABLE user_profiles ADD COLUMN IF NOT EXISTS deactivated_by_uuid UUID; + + +-- ============================================================================ +-- PHASE 2: Backfill UUID values from user_profiles join +-- ============================================================================ + +-- 2a. Feature tables: map user_id (auth0_sub) -> user_profiles.id (UUID) +UPDATE vehicles SET user_profile_id = up.id + FROM user_profiles up WHERE vehicles.user_id = up.auth0_sub AND vehicles.user_profile_id IS NULL; + +UPDATE fuel_logs SET user_profile_id = up.id + FROM user_profiles up WHERE fuel_logs.user_id = up.auth0_sub AND fuel_logs.user_profile_id IS NULL; + +UPDATE maintenance_records SET user_profile_id = up.id + FROM user_profiles up WHERE maintenance_records.user_id = up.auth0_sub AND maintenance_records.user_profile_id IS NULL; + +UPDATE maintenance_schedules SET user_profile_id = up.id + FROM user_profiles up WHERE maintenance_schedules.user_id = up.auth0_sub AND maintenance_schedules.user_profile_id IS NULL; + +UPDATE documents SET user_profile_id = up.id + FROM user_profiles up WHERE documents.user_id = up.auth0_sub AND documents.user_profile_id IS NULL; + +UPDATE notification_logs SET user_profile_id = up.id + FROM user_profiles up WHERE notification_logs.user_id = up.auth0_sub AND notification_logs.user_profile_id IS NULL; + +UPDATE user_notifications SET user_profile_id = up.id + FROM user_profiles up WHERE user_notifications.user_id = up.auth0_sub AND user_notifications.user_profile_id IS NULL; + +UPDATE user_preferences SET user_profile_id = up.id + FROM user_profiles up WHERE user_preferences.user_id = up.auth0_sub AND user_preferences.user_profile_id IS NULL; + +UPDATE saved_stations SET user_profile_id = up.id + FROM user_profiles up WHERE saved_stations.user_id = up.auth0_sub AND saved_stations.user_profile_id IS NULL; + +UPDATE audit_logs SET user_profile_id = up.id + FROM user_profiles up WHERE audit_logs.user_id = up.auth0_sub AND audit_logs.user_profile_id IS NULL; + +UPDATE ownership_costs SET user_profile_id = up.id + FROM user_profiles up WHERE ownership_costs.user_id = up.auth0_sub AND ownership_costs.user_profile_id IS NULL; + +UPDATE email_ingestion_queue SET user_profile_id = up.id + FROM user_profiles up WHERE email_ingestion_queue.user_id = up.auth0_sub AND email_ingestion_queue.user_profile_id IS NULL; + +UPDATE pending_vehicle_associations SET user_profile_id = up.id + FROM user_profiles up WHERE pending_vehicle_associations.user_id = up.auth0_sub AND pending_vehicle_associations.user_profile_id IS NULL; + +UPDATE subscriptions SET user_profile_id = up.id + FROM user_profiles up WHERE subscriptions.user_id = up.auth0_sub AND subscriptions.user_profile_id IS NULL; + +UPDATE donations SET user_profile_id = up.id + FROM user_profiles up WHERE donations.user_id = up.auth0_sub AND donations.user_profile_id IS NULL; + +UPDATE tier_vehicle_selections SET user_profile_id = up.id + FROM user_profiles up WHERE tier_vehicle_selections.user_id = up.auth0_sub AND tier_vehicle_selections.user_profile_id IS NULL; + +UPDATE terms_agreements SET user_profile_id = up.id + FROM user_profiles up WHERE terms_agreements.user_id = up.auth0_sub AND terms_agreements.user_profile_id IS NULL; + +-- 2b. Special user columns +UPDATE community_stations SET submitted_by_uuid = up.id + FROM user_profiles up WHERE community_stations.submitted_by = up.auth0_sub AND community_stations.submitted_by_uuid IS NULL; + +UPDATE station_removal_reports SET reported_by_uuid = up.id + FROM user_profiles up WHERE station_removal_reports.reported_by = up.auth0_sub AND station_removal_reports.reported_by_uuid IS NULL; + + +-- ============================================================================ +-- PHASE 3: Admin-specific transformations +-- ============================================================================ + +-- 3a. Create user_profiles entries for any admin_users that lack one +INSERT INTO user_profiles (auth0_sub, email) +SELECT au.auth0_sub, au.email +FROM admin_users au +WHERE NOT EXISTS ( + SELECT 1 FROM user_profiles up WHERE up.auth0_sub = au.auth0_sub +) +ON CONFLICT (auth0_sub) DO NOTHING; + +-- 3b. Populate admin_users.id (DEFAULT doesn't auto-fill on ALTER ADD COLUMN for existing rows) +UPDATE admin_users SET id = uuid_generate_v4() WHERE id IS NULL; + +-- 3c. Backfill admin_users.user_profile_id from user_profiles join +UPDATE admin_users SET user_profile_id = up.id + FROM user_profiles up WHERE admin_users.auth0_sub = up.auth0_sub AND admin_users.user_profile_id IS NULL; + +-- 3d. Backfill admin-referencing columns: map auth0_sub -> admin_users.id UUID +UPDATE admin_audit_logs SET actor_admin_uuid = au.id + FROM admin_users au WHERE admin_audit_logs.actor_admin_id = au.auth0_sub AND admin_audit_logs.actor_admin_uuid IS NULL; + +UPDATE admin_audit_logs SET target_admin_uuid = au.id + FROM admin_users au WHERE admin_audit_logs.target_admin_id = au.auth0_sub AND admin_audit_logs.target_admin_uuid IS NULL; + +UPDATE admin_users au SET created_by_uuid = creator.id + FROM admin_users creator WHERE au.created_by = creator.auth0_sub AND au.created_by_uuid IS NULL; + +UPDATE community_stations SET reviewed_by_uuid = au.id + FROM admin_users au WHERE community_stations.reviewed_by = au.auth0_sub AND community_stations.reviewed_by_uuid IS NULL; + +UPDATE backup_history SET created_by_uuid = au.id + FROM admin_users au WHERE backup_history.created_by = au.auth0_sub AND backup_history.created_by_uuid IS NULL; + +UPDATE platform_change_log SET changed_by_uuid = au.id + FROM admin_users au WHERE platform_change_log.changed_by = au.auth0_sub AND platform_change_log.changed_by_uuid IS NULL; + +UPDATE user_profiles SET deactivated_by_uuid = au.id + FROM admin_users au WHERE user_profiles.deactivated_by = au.auth0_sub AND user_profiles.deactivated_by_uuid IS NULL; + + +-- ============================================================================ +-- PHASE 4: Add constraints +-- ============================================================================ + +-- 4a. Set NOT NULL on feature table UUID columns (audit_logs stays nullable) +ALTER TABLE vehicles ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE fuel_logs ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE maintenance_records ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE maintenance_schedules ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE documents ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE notification_logs ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE user_notifications ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE user_preferences ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE saved_stations ALTER COLUMN user_profile_id SET NOT NULL; +-- audit_logs.user_profile_id stays NULLABLE (system actions have no user) +ALTER TABLE ownership_costs ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE email_ingestion_queue ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE pending_vehicle_associations ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE subscriptions ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE donations ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE tier_vehicle_selections ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE terms_agreements ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE community_stations ALTER COLUMN submitted_by_uuid SET NOT NULL; +ALTER TABLE station_removal_reports ALTER COLUMN reported_by_uuid SET NOT NULL; + +-- 4b. Admin table NOT NULL constraints +ALTER TABLE admin_users ALTER COLUMN id SET NOT NULL; +ALTER TABLE admin_users ALTER COLUMN user_profile_id SET NOT NULL; +ALTER TABLE admin_audit_logs ALTER COLUMN actor_admin_uuid SET NOT NULL; +-- target_admin_uuid stays nullable (some actions have no target) +-- created_by_uuid stays nullable (bootstrap admin may not have a creator) +ALTER TABLE platform_change_log ALTER COLUMN changed_by_uuid SET NOT NULL; + +-- 4c. Admin table PK transformation +ALTER TABLE admin_users DROP CONSTRAINT admin_users_pkey; +ALTER TABLE admin_users ADD PRIMARY KEY (id); + +-- 4d. Add FK constraints to user_profiles(id) with ON DELETE CASCADE +ALTER TABLE vehicles ADD CONSTRAINT fk_vehicles_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE fuel_logs ADD CONSTRAINT fk_fuel_logs_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE maintenance_records ADD CONSTRAINT fk_maintenance_records_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE maintenance_schedules ADD CONSTRAINT fk_maintenance_schedules_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE documents ADD CONSTRAINT fk_documents_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE notification_logs ADD CONSTRAINT fk_notification_logs_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE user_notifications ADD CONSTRAINT fk_user_notifications_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE user_preferences ADD CONSTRAINT fk_user_preferences_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE saved_stations ADD CONSTRAINT fk_saved_stations_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE audit_logs ADD CONSTRAINT fk_audit_logs_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE ownership_costs ADD CONSTRAINT fk_ownership_costs_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE email_ingestion_queue ADD CONSTRAINT fk_email_ingestion_queue_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE pending_vehicle_associations ADD CONSTRAINT fk_pending_vehicle_assoc_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE subscriptions ADD CONSTRAINT fk_subscriptions_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE donations ADD CONSTRAINT fk_donations_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE tier_vehicle_selections ADD CONSTRAINT fk_tier_vehicle_selections_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE terms_agreements ADD CONSTRAINT fk_terms_agreements_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE community_stations ADD CONSTRAINT fk_community_stations_submitted_by + FOREIGN KEY (submitted_by_uuid) REFERENCES user_profiles(id) ON DELETE CASCADE; +ALTER TABLE station_removal_reports ADD CONSTRAINT fk_station_removal_reports_reported_by + FOREIGN KEY (reported_by_uuid) REFERENCES user_profiles(id) ON DELETE CASCADE; + +-- 4e. Admin FK constraints +ALTER TABLE admin_users ADD CONSTRAINT fk_admin_users_user_profile_id + FOREIGN KEY (user_profile_id) REFERENCES user_profiles(id); +ALTER TABLE admin_users ADD CONSTRAINT uq_admin_users_user_profile_id + UNIQUE (user_profile_id); + + +-- ============================================================================ +-- PHASE 5: Drop old columns, rename new ones, recreate indexes +-- ============================================================================ + +-- 5a. Drop old FK constraints on VARCHAR user_id columns +ALTER TABLE subscriptions DROP CONSTRAINT IF EXISTS fk_subscriptions_user_id; +ALTER TABLE donations DROP CONSTRAINT IF EXISTS fk_donations_user_id; +ALTER TABLE tier_vehicle_selections DROP CONSTRAINT IF EXISTS fk_tier_vehicle_selections_user_id; + +-- 5b. Drop old UNIQUE constraints involving VARCHAR columns +ALTER TABLE vehicles DROP CONSTRAINT IF EXISTS unique_user_vin; +ALTER TABLE saved_stations DROP CONSTRAINT IF EXISTS unique_user_station; +ALTER TABLE user_preferences DROP CONSTRAINT IF EXISTS user_preferences_user_id_key; +ALTER TABLE station_removal_reports DROP CONSTRAINT IF EXISTS unique_user_station_report; + +-- 5c. Drop old indexes on VARCHAR columns +DROP INDEX IF EXISTS idx_vehicles_user_id; +DROP INDEX IF EXISTS idx_fuel_logs_user_id; +DROP INDEX IF EXISTS idx_maintenance_records_user_id; +DROP INDEX IF EXISTS idx_maintenance_schedules_user_id; +DROP INDEX IF EXISTS idx_documents_user_id; +DROP INDEX IF EXISTS idx_documents_user_vehicle; +DROP INDEX IF EXISTS idx_notification_logs_user_id; +DROP INDEX IF EXISTS idx_user_notifications_user_id; +DROP INDEX IF EXISTS idx_user_notifications_unread; +DROP INDEX IF EXISTS idx_user_preferences_user_id; +DROP INDEX IF EXISTS idx_saved_stations_user_id; +DROP INDEX IF EXISTS idx_audit_logs_user_created; +DROP INDEX IF EXISTS idx_ownership_costs_user_id; +DROP INDEX IF EXISTS idx_email_ingestion_queue_user_id; +DROP INDEX IF EXISTS idx_pending_vehicle_assoc_user_id; +DROP INDEX IF EXISTS idx_subscriptions_user_id; +DROP INDEX IF EXISTS idx_donations_user_id; +DROP INDEX IF EXISTS idx_tier_vehicle_selections_user_id; +DROP INDEX IF EXISTS idx_terms_agreements_user_id; +DROP INDEX IF EXISTS idx_community_stations_submitted_by; +DROP INDEX IF EXISTS idx_removal_reports_reported_by; +DROP INDEX IF EXISTS idx_admin_audit_logs_actor_id; +DROP INDEX IF EXISTS idx_admin_audit_logs_target_id; +DROP INDEX IF EXISTS idx_platform_change_log_changed_by; + +-- 5d. Drop old VARCHAR user_id columns from feature tables +ALTER TABLE vehicles DROP COLUMN user_id; +ALTER TABLE fuel_logs DROP COLUMN user_id; +ALTER TABLE maintenance_records DROP COLUMN user_id; +ALTER TABLE maintenance_schedules DROP COLUMN user_id; +ALTER TABLE documents DROP COLUMN user_id; +ALTER TABLE notification_logs DROP COLUMN user_id; +ALTER TABLE user_notifications DROP COLUMN user_id; +ALTER TABLE user_preferences DROP COLUMN user_id; +ALTER TABLE saved_stations DROP COLUMN user_id; +ALTER TABLE audit_logs DROP COLUMN user_id; +ALTER TABLE ownership_costs DROP COLUMN user_id; +ALTER TABLE email_ingestion_queue DROP COLUMN user_id; +ALTER TABLE pending_vehicle_associations DROP COLUMN user_id; +ALTER TABLE subscriptions DROP COLUMN user_id; +ALTER TABLE donations DROP COLUMN user_id; +ALTER TABLE tier_vehicle_selections DROP COLUMN user_id; +ALTER TABLE terms_agreements DROP COLUMN user_id; + +-- 5e. Rename user_profile_id -> user_id in feature tables +ALTER TABLE vehicles RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE fuel_logs RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE maintenance_records RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE maintenance_schedules RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE documents RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE notification_logs RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE user_notifications RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE user_preferences RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE saved_stations RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE audit_logs RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE ownership_costs RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE email_ingestion_queue RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE pending_vehicle_associations RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE subscriptions RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE donations RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE tier_vehicle_selections RENAME COLUMN user_profile_id TO user_id; +ALTER TABLE terms_agreements RENAME COLUMN user_profile_id TO user_id; + +-- 5f. Drop and rename special user columns +ALTER TABLE community_stations DROP COLUMN submitted_by; +ALTER TABLE community_stations RENAME COLUMN submitted_by_uuid TO submitted_by; +ALTER TABLE station_removal_reports DROP COLUMN reported_by; +ALTER TABLE station_removal_reports RENAME COLUMN reported_by_uuid TO reported_by; + +-- 5g. Drop and rename admin-referencing columns +ALTER TABLE admin_users DROP COLUMN auth0_sub; +ALTER TABLE admin_users DROP COLUMN created_by; +ALTER TABLE admin_users RENAME COLUMN created_by_uuid TO created_by; + +ALTER TABLE admin_audit_logs DROP COLUMN actor_admin_id; +ALTER TABLE admin_audit_logs DROP COLUMN target_admin_id; +ALTER TABLE admin_audit_logs RENAME COLUMN actor_admin_uuid TO actor_admin_id; +ALTER TABLE admin_audit_logs RENAME COLUMN target_admin_uuid TO target_admin_id; + +ALTER TABLE community_stations DROP COLUMN reviewed_by; +ALTER TABLE community_stations RENAME COLUMN reviewed_by_uuid TO reviewed_by; + +ALTER TABLE backup_history DROP COLUMN created_by; +ALTER TABLE backup_history RENAME COLUMN created_by_uuid TO created_by; + +ALTER TABLE platform_change_log DROP COLUMN changed_by; +ALTER TABLE platform_change_log RENAME COLUMN changed_by_uuid TO changed_by; + +ALTER TABLE user_profiles DROP COLUMN deactivated_by; +ALTER TABLE user_profiles RENAME COLUMN deactivated_by_uuid TO deactivated_by; + +-- 5h. Recreate indexes on new UUID columns (feature tables) +CREATE INDEX idx_vehicles_user_id ON vehicles(user_id); +CREATE INDEX idx_fuel_logs_user_id ON fuel_logs(user_id); +CREATE INDEX idx_maintenance_records_user_id ON maintenance_records(user_id); +CREATE INDEX idx_maintenance_schedules_user_id ON maintenance_schedules(user_id); +CREATE INDEX idx_documents_user_id ON documents(user_id); +CREATE INDEX idx_documents_user_vehicle ON documents(user_id, vehicle_id); +CREATE INDEX idx_notification_logs_user_id ON notification_logs(user_id); +CREATE INDEX idx_user_notifications_user_id ON user_notifications(user_id); +CREATE INDEX idx_user_notifications_unread ON user_notifications(user_id, created_at DESC) WHERE is_read = false; +CREATE INDEX idx_user_preferences_user_id ON user_preferences(user_id); +CREATE INDEX idx_saved_stations_user_id ON saved_stations(user_id); +CREATE INDEX idx_audit_logs_user_created ON audit_logs(user_id, created_at DESC); +CREATE INDEX idx_ownership_costs_user_id ON ownership_costs(user_id); +CREATE INDEX idx_email_ingestion_queue_user_id ON email_ingestion_queue(user_id); +CREATE INDEX idx_pending_vehicle_assoc_user_id ON pending_vehicle_associations(user_id); +CREATE INDEX idx_subscriptions_user_id ON subscriptions(user_id); +CREATE INDEX idx_donations_user_id ON donations(user_id); +CREATE INDEX idx_tier_vehicle_selections_user_id ON tier_vehicle_selections(user_id); +CREATE INDEX idx_terms_agreements_user_id ON terms_agreements(user_id); + +-- 5i. Recreate indexes on special columns +CREATE INDEX idx_community_stations_submitted_by ON community_stations(submitted_by); +CREATE INDEX idx_removal_reports_reported_by ON station_removal_reports(reported_by); +CREATE INDEX idx_admin_audit_logs_actor_id ON admin_audit_logs(actor_admin_id); +CREATE INDEX idx_admin_audit_logs_target_id ON admin_audit_logs(target_admin_id); +CREATE INDEX idx_platform_change_log_changed_by ON platform_change_log(changed_by); + +-- 5j. Recreate UNIQUE constraints on new UUID columns +ALTER TABLE vehicles ADD CONSTRAINT unique_user_vin UNIQUE(user_id, vin); +ALTER TABLE saved_stations ADD CONSTRAINT unique_user_station UNIQUE(user_id, place_id); +ALTER TABLE user_preferences ADD CONSTRAINT user_preferences_user_id_key UNIQUE(user_id); +ALTER TABLE station_removal_reports ADD CONSTRAINT unique_user_station_report UNIQUE(station_id, reported_by); + +COMMIT;