fix(migrations): heal orphaned auth.users before account backfill (017)

The pre-017 signup trigger (001) inserted the profiles row inside an
`EXCEPTION WHEN OTHERS ... RAISE WARNING; RETURN NEW` block, so a signup
could leave an auth.users row with no matching profiles row.

The 017 account-sharing backfill keyed accounts off `profiles`, so such
an orphaned user got no account and was never linked. If that user owned
any domain rows (pre-017 RLS only required `auth.uid() = user_id`, not a
profile), their account_id stayed NULL and the subsequent
`ALTER TABLE ... SET NOT NULL` aborted the whole migration — exactly the
upgrade failure reported in #274.

Add a step (0) to the backfill DO block that creates the missing profile
for every auth.users row lacking one, before accounts are created. This
keys the backfill off auth.users instead of profiles, so every
authenticated user is migrated and no domain row can be left without an
account. Idempotent and safe to re-run, matching the rest of 017.

Fixes #274

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Arnas Donauskas 2026-06-22 09:33:42 +03:00
parent e714fb0db5
commit 38d929bbbb
No known key found for this signature in database

View File

@ -193,6 +193,7 @@ ALTER TABLE flow_runs ADD COLUMN IF NOT EXISTS account_id U
-- BACKFILL -- BACKFILL
-- --
-- Order is load-bearing: -- Order is load-bearing:
-- 0. Heal orphaned auth.users that never got a profile row.
-- 1. Create one account per existing profile (the existing user -- 1. Create one account per existing profile (the existing user
-- is the owner). -- is the owner).
-- 2. Stamp profile.account_id / account_role from the row above. -- 2. Stamp profile.account_id / account_role from the row above.
@ -214,6 +215,26 @@ DECLARE
'flows', 'flow_runs' 'flows', 'flow_runs'
]; ];
BEGIN BEGIN
-- (0) Heal orphaned users. The pre-017 signup trigger (migration
-- 001) inserted the profile inside an `EXCEPTION WHEN OTHERS ...
-- RAISE WARNING; RETURN NEW` block, so a signup could leave an
-- auth.users row with no matching profiles row. Those orphans would
-- be skipped by step (1) below, get no account, and — if they own
-- any domain rows (pre-017 RLS only required auth.uid() = user_id,
-- not a profile) — leave account_id NULL and abort the SET NOT NULL
-- step. Backfilling the missing profile first keys the whole backfill
-- off auth.users instead of profiles, so every authenticated user is
-- migrated and no domain row can be left without an account.
-- full_name / email are NOT NULL on profiles, hence the COALESCE.
INSERT INTO public.profiles (user_id, full_name, email)
SELECT u.id,
COALESCE(u.raw_user_meta_data->>'full_name', ''),
COALESCE(u.email, '')
FROM auth.users u
WHERE NOT EXISTS (
SELECT 1 FROM public.profiles p WHERE p.user_id = u.id
);
-- (1) Create one account per existing profile whose user does not -- (1) Create one account per existing profile whose user does not
-- yet own one. Idempotent: skips users that already have an account. -- yet own one. Idempotent: skips users that already have an account.
INSERT INTO accounts (name, owner_user_id) INSERT INTO accounts (name, owner_user_id)