From 149458d97ffbbe736e4bb2d23b22ab549d671cf1 Mon Sep 17 00:00:00 2001 From: shenlan Date: Sun, 5 Oct 2025 10:10:21 +0800 Subject: [PATCH] chore: remove legacy account migrations (#404) --- account/sql/20251001-migrate_to_uuid.md | 94 +-- account/sql/20251001-migrate_to_uuid.sql | 210 ----- account/sql/20251001-switch_uuid.sql | 28 - account/sql/20251002-add-mfa-columns.sql | 6 - .../sql/20251004-ensure-uuid-schema.down.sql | 73 -- .../sql/20251004-ensure-uuid-schema.up.sql | 732 ------------------ 6 files changed, 43 insertions(+), 1100 deletions(-) delete mode 100644 account/sql/20251001-migrate_to_uuid.sql delete mode 100644 account/sql/20251001-switch_uuid.sql delete mode 100644 account/sql/20251002-add-mfa-columns.sql delete mode 100644 account/sql/20251004-ensure-uuid-schema.down.sql delete mode 100644 account/sql/20251004-ensure-uuid-schema.up.sql diff --git a/account/sql/20251001-migrate_to_uuid.md b/account/sql/20251001-migrate_to_uuid.md index 2a775de..14163e6 100644 --- a/account/sql/20251001-migrate_to_uuid.md +++ b/account/sql/20251001-migrate_to_uuid.md @@ -1,68 +1,60 @@ -# 前置准备 +# 历史 UUID 迁移指引(已归档) -1. 确认扩展 -登录 PostgreSQL,执行: \dx 看看是否已经有 uuid-ossp 或 pgcrypto。 -如果没有,就执行: +> **说明**:项目已经统一改为直接使用 `schema.sql` 初始化数据库,不再提供单独的 UUID 迁移脚本。本指南仅保留作为旧环境排查的参考,若你是全新部署,可直接执行 `schema.sql` 并忽略本文件。 -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -或者: -CREATE EXTENSION IF NOT EXISTS "pgcrypto"; +1. **确认扩展** + 登录 PostgreSQL,执行 `\dx` 检查是否已经启用了 `uuid-ossp` 或 `pgcrypto`。若没有,可执行: + ```sql + CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + CREATE EXTENSION IF NOT EXISTS "pgcrypto"; + ``` -⚠️ 推荐用 pgcrypto(函数是 gen_random_uuid()),更现代; -如果你已经用了 uuid-ossp,脚本里的 uuid_generate_v4() 就能直接用。 + 推荐使用 `pgcrypto` 的 `gen_random_uuid()`;若历史库已启用 `uuid-ossp`,脚本中的 `uuid_generate_v4()` 亦可直接使用。 -2. 备份数据库 +2. **备份数据库** + 任何迁移前都应留存快照: -这是最关键的保险: + ```bash + pg_dump -U -d > backup_before_uuid.sql + ``` -pg_dump -U -d > backup_before_uuid.sql +3. **执行迁移脚本(仅旧项目)** + 旧版项目曾通过 `migrate_to_uuid.sql` 完成以下步骤: + - 新增 `uuid` 主键列并填充现有数据; + - 添加 `user_uuid` 外键字段,与旧的自增 `id` 并行; + - 删除旧的 `id`、`user_id` 列并将 `uuid` 设为主键。 -3. 执行迁移 + 如仍需在遗留环境执行,可根据上述逻辑自行编写脚本,或从历史提交中找回旧版 SQL。 -假设你的 schema 在 schema.sql 文件里,迁移脚本在 migrate_to_uuid.sql 文件里。 +4. **验证结果** + 迁移完成后进入数据库(`psql -U -d `)并检查: -执行: psql -U -d -f migrate_to_uuid.sql + ```sql + \d users + \d identities + \d sessions + ``` + 预期结构示例: -这个脚本会做三步: + - `users(uuid PRIMARY KEY, username, password, email, created_at …)` + - `identities(uuid PRIMARY KEY, user_uuid REFERENCES users(uuid), provider, external_id …)` + - `sessions(uuid PRIMARY KEY, user_uuid REFERENCES users(uuid), token, expires_at …)` -- 新增 uuid 列,填充数据。 -- 新建 user_uuid 外键字段,保持和旧 id 外键并存。 -- 删除旧的 id、user_id,把 uuid 设为主键。 + 亦可通过信息架构核对外键: -4. 验证结果 + ```sql + SELECT constraint_name, table_name, column_name, foreign_table_name + FROM information_schema.key_column_usage + WHERE table_schema = 'public'; + ``` -迁移后,进入数据库: psql -U -d +5. **回滚策略** + 若迁移出现问题,可使用备份恢复: -查看表结构: + ```bash + psql -U -d < backup_before_uuid.sql + ``` -\d users -\d identities -\d sessions - - -应该看到: - -users(uuid PRIMARY KEY, username, password, email, created_at …) -identities(uuid PRIMARY KEY, user_uuid REFERENCES users(uuid), provider, external_id …) -sessions(uuid PRIMARY KEY, user_uuid REFERENCES users(uuid), token, expires_at …) - -再验证外键是否正确: - -SELECT constraint_name, table_name, column_name, foreign_table_name -FROM information_schema.key_column_usage -WHERE table_schema = 'public'; - -回滚策略 - -如果迁移出错,可以用之前的备份恢复: - -psql -U -d < backup_before_uuid.sql - -总结: - -先启用扩展 -再运行迁移脚本 -验证新表结构和外键 -保留备份,随时可回滚 +> **小结**:新部署只需运行 `schema.sql`。旧环境若要沿用 UUID 主键改造,可参考上述思路自行调整脚本,并务必在操作前备份。 diff --git a/account/sql/20251001-migrate_to_uuid.sql b/account/sql/20251001-migrate_to_uuid.sql deleted file mode 100644 index 6a429d3..0000000 --- a/account/sql/20251001-migrate_to_uuid.sql +++ /dev/null @@ -1,210 +0,0 @@ ------------------------------------------------- --- STEP 0: 启用 UUID 扩展 ------------------------------------------------- --- 两种方式任选其一,推荐 pgcrypto(更现代) --- 如果数据库还没装扩展,可以先运行 CREATE EXTENSION - --- 方式一:使用 uuid-ossp -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; - --- 方式二:使用 pgcrypto --- CREATE EXTENSION IF NOT EXISTS "pgcrypto"; - --- 注意: --- uuid-ossp 用 uuid_generate_v4() --- pgcrypto 用 gen_random_uuid() - ------------------------------------------------- --- STEP 1: 平滑迁移(添加 UUID 字段并建立外键) ------------------------------------------------- - --- ========== Users ========== -ALTER TABLE users - ADD COLUMN IF NOT EXISTS uuid UUID DEFAULT uuid_generate_v4(); - -UPDATE users SET uuid = uuid_generate_v4() WHERE uuid IS NULL; - -ALTER TABLE users - ALTER COLUMN uuid SET NOT NULL; - -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM pg_constraint - WHERE conrelid = 'users'::regclass - AND conname = 'users_uuid_unique' - ) THEN - EXECUTE 'ALTER TABLE users ADD CONSTRAINT users_uuid_unique UNIQUE (uuid)'; - END IF; -END $$; - --- ========== Identities ========== -ALTER TABLE identities - ADD COLUMN IF NOT EXISTS uuid UUID DEFAULT uuid_generate_v4(); - -UPDATE identities SET uuid = uuid_generate_v4() WHERE uuid IS NULL; - -ALTER TABLE identities - ALTER COLUMN uuid SET NOT NULL; - -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM pg_constraint - WHERE conrelid = 'identities'::regclass - AND conname = 'identities_uuid_unique' - ) THEN - EXECUTE 'ALTER TABLE identities ADD CONSTRAINT identities_uuid_unique UNIQUE (uuid)'; - END IF; -END $$; - --- 新增 user_uuid 外键字段 -ALTER TABLE identities - ADD COLUMN IF NOT EXISTS user_uuid UUID; - -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_name = 'identities' - AND column_name = 'user_id' - AND table_schema = 'public' - ) THEN - EXECUTE 'UPDATE identities i SET user_uuid = u.uuid FROM users u WHERE i.user_id = u.id;'; - END IF; -END $$; - -ALTER TABLE identities - ALTER COLUMN user_uuid SET NOT NULL; - -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM pg_constraint - WHERE conrelid = 'identities'::regclass - AND conname = 'identities_user_uuid_fk' - ) THEN - EXECUTE 'ALTER TABLE identities ADD CONSTRAINT identities_user_uuid_fk FOREIGN KEY (user_uuid) REFERENCES users(uuid) ON DELETE CASCADE'; - END IF; -END $$; - --- ========== Sessions ========== -ALTER TABLE sessions - ADD COLUMN IF NOT EXISTS uuid UUID DEFAULT uuid_generate_v4(); - -UPDATE sessions SET uuid = uuid_generate_v4() WHERE uuid IS NULL; - -ALTER TABLE sessions - ALTER COLUMN uuid SET NOT NULL; - -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM pg_constraint - WHERE conrelid = 'sessions'::regclass - AND conname = 'sessions_uuid_unique' - ) THEN - EXECUTE 'ALTER TABLE sessions ADD CONSTRAINT sessions_uuid_unique UNIQUE (uuid)'; - END IF; -END $$; - --- 新增 user_uuid 外键字段 -ALTER TABLE sessions - ADD COLUMN IF NOT EXISTS user_uuid UUID; - -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_name = 'sessions' - AND column_name = 'user_id' - AND table_schema = 'public' - ) THEN - EXECUTE 'UPDATE sessions s SET user_uuid = u.uuid FROM users u WHERE s.user_id = u.id;'; - END IF; -END $$; - -ALTER TABLE sessions - ALTER COLUMN user_uuid SET NOT NULL; - -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM pg_constraint - WHERE conrelid = 'sessions'::regclass - AND conname = 'sessions_user_uuid_fk' - ) THEN - EXECUTE 'ALTER TABLE sessions ADD CONSTRAINT sessions_user_uuid_fk FOREIGN KEY (user_uuid) REFERENCES users(uuid) ON DELETE CASCADE'; - END IF; -END $$; - - ------------------------------------------------- --- STEP 2: 清理(彻底切换到 UUID 主键) ------------------------------------------------- - --- 删除原有的主键约束 -ALTER TABLE users DROP CONSTRAINT IF EXISTS users_pkey; -ALTER TABLE identities DROP CONSTRAINT IF EXISTS identities_pkey; -ALTER TABLE sessions DROP CONSTRAINT IF EXISTS sessions_pkey; - --- 删除旧的 id 外键 -ALTER TABLE identities DROP CONSTRAINT IF EXISTS identities_user_id_fkey; -ALTER TABLE sessions DROP CONSTRAINT IF EXISTS sessions_user_id_fkey; - --- 设置 uuid 为新的主键 -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM pg_constraint - WHERE conrelid = 'users'::regclass - AND conname = 'users_pkey' - ) THEN - EXECUTE 'ALTER TABLE users ADD CONSTRAINT users_pkey PRIMARY KEY (uuid)'; - END IF; -END $$; - -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM pg_constraint - WHERE conrelid = 'identities'::regclass - AND conname = 'identities_pkey' - ) THEN - EXECUTE 'ALTER TABLE identities ADD CONSTRAINT identities_pkey PRIMARY KEY (uuid)'; - END IF; -END $$; - -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM pg_constraint - WHERE conrelid = 'sessions'::regclass - AND conname = 'sessions_pkey' - ) THEN - EXECUTE 'ALTER TABLE sessions ADD CONSTRAINT sessions_pkey PRIMARY KEY (uuid)'; - END IF; -END $$; - --- 删除旧的 id 字段 -ALTER TABLE users DROP COLUMN IF EXISTS id; -ALTER TABLE identities DROP COLUMN IF EXISTS id; -ALTER TABLE sessions DROP COLUMN IF EXISTS id; - --- 删除旧的 user_id 外键字段 -ALTER TABLE identities DROP COLUMN IF EXISTS user_id; -ALTER TABLE sessions DROP COLUMN IF EXISTS user_id; - ------------------------------------------------- --- Done: 所有表都只用 UUID 作为主键/外键 ------------------------------------------------- diff --git a/account/sql/20251001-switch_uuid.sql b/account/sql/20251001-switch_uuid.sql deleted file mode 100644 index 703ca0a..0000000 --- a/account/sql/20251001-switch_uuid.sql +++ /dev/null @@ -1,28 +0,0 @@ - ------------------------------------------------- --- STEP 2: 切换到 UUID-only ------------------------------------------------- - --- 删除旧外键(如果还在的话) -ALTER TABLE identities DROP CONSTRAINT IF EXISTS identities_user_id_fkey; -ALTER TABLE sessions DROP CONSTRAINT IF EXISTS sessions_user_id_fkey; - --- 删除旧主键 -ALTER TABLE users DROP CONSTRAINT IF EXISTS users_pkey; -ALTER TABLE identities DROP CONSTRAINT IF EXISTS identities_pkey; -ALTER TABLE sessions DROP CONSTRAINT IF EXISTS sessions_pkey; - --- 设置 uuid 为主键 -ALTER TABLE users ADD CONSTRAINT users_pkey PRIMARY KEY (uuid); -ALTER TABLE identities ADD CONSTRAINT identities_pkey PRIMARY KEY (uuid); -ALTER TABLE sessions ADD CONSTRAINT sessions_pkey PRIMARY KEY (uuid); - --- 删除旧 id 字段(如果还存在) -ALTER TABLE users DROP COLUMN IF EXISTS id; -ALTER TABLE identities DROP COLUMN IF EXISTS id; -ALTER TABLE sessions DROP COLUMN IF EXISTS id; - --- 删除旧的 user_id 外键字段(如果还存在) -ALTER TABLE identities DROP COLUMN IF EXISTS user_id; -ALTER TABLE sessions DROP COLUMN IF EXISTS user_id; - diff --git a/account/sql/20251002-add-mfa-columns.sql b/account/sql/20251002-add-mfa-columns.sql deleted file mode 100644 index 615b56c..0000000 --- a/account/sql/20251002-add-mfa-columns.sql +++ /dev/null @@ -1,6 +0,0 @@ -ALTER TABLE users - ADD COLUMN IF NOT EXISTS mfa_totp_secret TEXT, - ADD COLUMN IF NOT EXISTS mfa_enabled BOOLEAN NOT NULL DEFAULT FALSE, - ADD COLUMN IF NOT EXISTS mfa_secret_issued_at TIMESTAMPTZ, - ADD COLUMN IF NOT EXISTS mfa_confirmed_at TIMESTAMPTZ, - ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ DEFAULT now(); diff --git a/account/sql/20251004-ensure-uuid-schema.down.sql b/account/sql/20251004-ensure-uuid-schema.down.sql deleted file mode 100644 index e1de2de..0000000 --- a/account/sql/20251004-ensure-uuid-schema.down.sql +++ /dev/null @@ -1,73 +0,0 @@ --- Drop trigger before removing supporting function -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM pg_trigger - WHERE tgname = 'trg_users_set_updated_at' - AND tgrelid = 'public.users'::regclass - ) THEN - DROP TRIGGER trg_users_set_updated_at ON public.users; - END IF; -END -$$; - -DROP FUNCTION IF EXISTS public.set_updated_at(); - --- Drop indexes introduced by the migration -DROP INDEX IF EXISTS public.idx_sessions_user_uuid; -DROP INDEX IF EXISTS public.idx_identities_provider; -DROP INDEX IF EXISTS public.idx_identities_user_uuid; - --- Drop foreign keys -ALTER TABLE public.sessions - DROP CONSTRAINT IF EXISTS sessions_user_fk; - -ALTER TABLE public.identities - DROP CONSTRAINT IF EXISTS identities_user_fk; - --- Drop unique constraints -ALTER TABLE public.identities - DROP CONSTRAINT IF EXISTS identities_provider_external_id_uk; - -ALTER TABLE public.users - DROP CONSTRAINT IF EXISTS users_username_uk; - -ALTER TABLE public.users - DROP CONSTRAINT IF EXISTS users_email_uk; - --- Remove generated column but retain supporting timestamps -ALTER TABLE public.users - DROP COLUMN IF EXISTS email_verified; - - -ALTER TABLE public.users - ALTER COLUMN updated_at DROP DEFAULT; - - --- Restore uuid columns to neutral defaults -ALTER TABLE public.sessions - ALTER COLUMN uuid DROP DEFAULT, - ALTER COLUMN uuid DROP NOT NULL; - -ALTER TABLE public.identities - ALTER COLUMN uuid DROP DEFAULT, - ALTER COLUMN uuid DROP NOT NULL, - ALTER COLUMN user_uuid DROP NOT NULL; - -ALTER TABLE public.users - ALTER COLUMN uuid DROP DEFAULT, - ALTER COLUMN uuid DROP NOT NULL; - -ALTER TABLE public.sessions - ALTER COLUMN user_uuid DROP NOT NULL; - --- Drop primary keys added by the migration -ALTER TABLE public.sessions - DROP CONSTRAINT IF EXISTS sessions_pkey; - -ALTER TABLE public.identities - DROP CONSTRAINT IF EXISTS identities_pkey; - -ALTER TABLE public.users - DROP CONSTRAINT IF EXISTS users_pkey; diff --git a/account/sql/20251004-ensure-uuid-schema.up.sql b/account/sql/20251004-ensure-uuid-schema.up.sql deleted file mode 100644 index 688844d..0000000 --- a/account/sql/20251004-ensure-uuid-schema.up.sql +++ /dev/null @@ -1,732 +0,0 @@ -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -CREATE EXTENSION IF NOT EXISTS "pgcrypto"; - --- USERS ------------------------------------------------------------------ -ALTER TABLE public.users - ADD COLUMN IF NOT EXISTS uuid uuid; - -UPDATE public.users -SET uuid = gen_random_uuid() -WHERE uuid IS NULL; - -ALTER TABLE public.users - ALTER COLUMN uuid TYPE uuid USING uuid::uuid; - -ALTER TABLE public.users - ALTER COLUMN uuid SET DEFAULT gen_random_uuid(); - -ALTER TABLE public.users - ALTER COLUMN uuid SET NOT NULL; - --- Ensure uuid columns are of the UUID type -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_schema = 'public' - AND table_name = 'users' - AND column_name = 'uuid' - AND udt_name <> 'uuid' - ) THEN - ALTER TABLE public.users - ALTER COLUMN uuid TYPE uuid USING uuid::uuid; - END IF; -END -$$; - -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_schema = 'public' - AND table_name = 'identities' - AND column_name = 'uuid' - AND udt_name <> 'uuid' - ) THEN - ALTER TABLE public.identities - ALTER COLUMN uuid TYPE uuid USING uuid::uuid; - END IF; -END -$$; - -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_schema = 'public' - AND table_name = 'identities' - AND column_name = 'user_uuid' - AND udt_name <> 'uuid' - ) THEN - ALTER TABLE public.identities - ALTER COLUMN user_uuid TYPE uuid USING user_uuid::uuid; - END IF; -END -$$; - -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_schema = 'public' - AND table_name = 'sessions' - AND column_name = 'uuid' - AND udt_name <> 'uuid' - ) THEN - ALTER TABLE public.sessions - ALTER COLUMN uuid TYPE uuid USING uuid::uuid; - END IF; -END -$$; - -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_schema = 'public' - AND table_name = 'sessions' - AND column_name = 'user_uuid' - AND udt_name <> 'uuid' - ) THEN - ALTER TABLE public.sessions - ALTER COLUMN user_uuid TYPE uuid USING user_uuid::uuid; - END IF; -END -$$; - --- Fill missing UUIDs before enforcing constraints -UPDATE public.users SET uuid = gen_random_uuid() WHERE uuid IS NULL; -UPDATE public.identities SET uuid = gen_random_uuid() WHERE uuid IS NULL; -UPDATE public.sessions SET uuid = gen_random_uuid() WHERE uuid IS NULL; - --- Ensure NOT NULL on uuid columns -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_schema = 'public' - AND table_name = 'users' - AND column_name = 'uuid' - AND is_nullable = 'YES' - ) THEN - ALTER TABLE public.users - ALTER COLUMN uuid SET NOT NULL; - END IF; -END -$$; - -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_schema = 'public' - AND table_name = 'identities' - AND column_name = 'uuid' - AND is_nullable = 'YES' - ) THEN - ALTER TABLE public.identities - ALTER COLUMN uuid SET NOT NULL; - END IF; -END -$$; - -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_schema = 'public' - AND table_name = 'sessions' - AND column_name = 'uuid' - AND is_nullable = 'YES' - ) THEN - ALTER TABLE public.sessions - ALTER COLUMN uuid SET NOT NULL; - END IF; -END -$$; - -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_schema = 'public' - AND table_name = 'identities' - AND column_name = 'user_uuid' - AND is_nullable = 'YES' - ) THEN - ALTER TABLE public.identities - ALTER COLUMN user_uuid SET NOT NULL; - END IF; -END -$$; - -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_schema = 'public' - AND table_name = 'sessions' - AND column_name = 'user_uuid' - AND is_nullable = 'YES' - ) THEN - ALTER TABLE public.sessions - ALTER COLUMN user_uuid SET NOT NULL; - END IF; -END -$$; - --- Ensure defaults for uuid columns -DO $$ -DECLARE - current_default text; - target_attnum int; -BEGIN - SELECT attnum INTO target_attnum - FROM pg_attribute - WHERE attrelid = 'public.users'::regclass - AND attname = 'uuid' - AND NOT attisdropped; - - IF target_attnum IS NOT NULL THEN - SELECT pg_get_expr(adbin, adrelid) - INTO current_default - FROM pg_attrdef - WHERE adrelid = 'public.users'::regclass - AND adnum = target_attnum; - - IF current_default IS DISTINCT FROM 'gen_random_uuid()' THEN - ALTER TABLE public.users - ALTER COLUMN uuid SET DEFAULT gen_random_uuid(); - END IF; - END IF; -END -$$; - -DO $$ -DECLARE - current_default text; - target_attnum int; -BEGIN - SELECT attnum INTO target_attnum - FROM pg_attribute - WHERE attrelid = 'public.identities'::regclass - AND attname = 'uuid' - AND NOT attisdropped; - - IF target_attnum IS NOT NULL THEN - SELECT pg_get_expr(adbin, adrelid) - INTO current_default - FROM pg_attrdef - WHERE adrelid = 'public.identities'::regclass - AND adnum = target_attnum; - - IF current_default IS DISTINCT FROM 'gen_random_uuid()' THEN - ALTER TABLE public.identities - ALTER COLUMN uuid SET DEFAULT gen_random_uuid(); - END IF; - END IF; -END -$$; - -DO $$ -DECLARE - current_default text; - target_attnum int; -BEGIN - SELECT attnum INTO target_attnum - FROM pg_attribute - WHERE attrelid = 'public.sessions'::regclass - AND attname = 'uuid' - AND NOT attisdropped; - - IF target_attnum IS NOT NULL THEN - SELECT pg_get_expr(adbin, adrelid) - INTO current_default - FROM pg_attrdef - WHERE adrelid = 'public.sessions'::regclass - AND adnum = target_attnum; - - IF current_default IS DISTINCT FROM 'gen_random_uuid()' THEN - ALTER TABLE public.sessions - ALTER COLUMN uuid SET DEFAULT gen_random_uuid(); - END IF; - END IF; -END -$$; - - -ALTER TABLE public.users - ADD COLUMN IF NOT EXISTS email_verified_at timestamptz; - -ALTER TABLE public.users - ADD COLUMN IF NOT EXISTS updated_at timestamptz; - -UPDATE public.users - -SET updated_at = COALESCE(updated_at, now()); - -DO $$ -DECLARE - target_attnum int; - current_default text; -BEGIN - SELECT attnum INTO target_attnum - FROM pg_attribute - WHERE attrelid = 'public.users'::regclass - AND attname = 'updated_at' - AND NOT attisdropped; - - IF target_attnum IS NOT NULL THEN - SELECT pg_get_expr(adbin, adrelid) - INTO current_default - FROM pg_attrdef - WHERE adrelid = 'public.users'::regclass - AND adnum = target_attnum; - - IF current_default IS DISTINCT FROM 'now()' THEN - ALTER TABLE public.users - ALTER COLUMN updated_at SET DEFAULT now(); - END IF; - END IF; -END -$$; - -DO $$ -DECLARE - att_generated char(1); -BEGIN - SELECT a.attgenerated - INTO att_generated - FROM pg_attribute a - WHERE a.attrelid = 'public.users'::regclass - AND a.attname = 'email_verified' - AND NOT a.attisdropped; - - IF att_generated IS NULL THEN - EXECUTE 'ALTER TABLE public.users ADD COLUMN email_verified boolean GENERATED ALWAYS AS (email_verified_at IS NOT NULL) STORED'; - ELSIF att_generated <> 's' THEN - EXECUTE 'ALTER TABLE public.users DROP COLUMN email_verified'; - EXECUTE 'ALTER TABLE public.users ADD COLUMN email_verified boolean GENERATED ALWAYS AS (email_verified_at IS NOT NULL) STORED'; - END IF; -END -$$; - -CREATE OR REPLACE FUNCTION public.set_updated_at() -RETURNS trigger -LANGUAGE plpgsql -AS $$ -BEGIN - NEW.updated_at := now(); - RETURN NEW; -END; -$$; - - -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM pg_trigger - WHERE tgname = 'trg_users_set_updated_at' - AND tgrelid = 'public.users'::regclass - AND NOT tgisinternal - ) THEN - CREATE TRIGGER trg_users_set_updated_at - BEFORE UPDATE ON public.users - FOR EACH ROW - EXECUTE FUNCTION public.set_updated_at(); - END IF; -END -$$; - -DO $$ -DECLARE - pk_name text; -BEGIN - SELECT conname INTO pk_name - FROM pg_constraint - WHERE conrelid = 'public.users'::regclass - AND contype = 'p' - LIMIT 1; - - IF pk_name IS NULL THEN - ALTER TABLE public.users - ADD CONSTRAINT users_pkey PRIMARY KEY (uuid); - ELSIF pk_name <> 'users_pkey' THEN - EXECUTE format('ALTER TABLE public.users RENAME CONSTRAINT %I TO users_pkey', pk_name); - END IF; -END -$$; - - -DO $$ -DECLARE - constraint_name text; - email_att smallint; -BEGIN - SELECT attnum INTO email_att - FROM pg_attribute - WHERE attrelid = 'public.users'::regclass - AND attname = 'email' - AND NOT attisdropped; - - IF email_att IS NOT NULL THEN - SELECT conname INTO constraint_name - FROM pg_constraint - WHERE conrelid = 'public.users'::regclass - AND contype = 'u' - AND conkey = ARRAY[email_att] - LIMIT 1; - - IF constraint_name IS NULL THEN - ALTER TABLE public.users - ADD CONSTRAINT users_email_uk UNIQUE (email); - ELSIF constraint_name <> 'users_email_uk' THEN - EXECUTE format('ALTER TABLE public.users RENAME CONSTRAINT %I TO users_email_uk', constraint_name); - END IF; - END IF; -END -$$; - -DO $$ -DECLARE - constraint_name text; - username_att smallint; -BEGIN - SELECT attnum INTO username_att - FROM pg_attribute - WHERE attrelid = 'public.users'::regclass - AND attname = 'username' - AND NOT attisdropped; - - IF username_att IS NOT NULL THEN - SELECT conname INTO constraint_name - FROM pg_constraint - WHERE conrelid = 'public.users'::regclass - AND contype = 'u' - AND conkey = ARRAY[username_att] - LIMIT 1; - - IF constraint_name IS NULL THEN - ALTER TABLE public.users - ADD CONSTRAINT users_username_uk UNIQUE (username); - ELSIF constraint_name <> 'users_username_uk' THEN - EXECUTE format('ALTER TABLE public.users RENAME CONSTRAINT %I TO users_username_uk', constraint_name); - END IF; - END IF; -END -$$; - - --- IDENTITIES -------------------------------------------------------------- -ALTER TABLE public.identities - ADD COLUMN IF NOT EXISTS uuid uuid; - -UPDATE public.identities -SET uuid = gen_random_uuid() -WHERE uuid IS NULL; - -ALTER TABLE public.identities - ALTER COLUMN uuid TYPE uuid USING uuid::uuid; - -ALTER TABLE public.identities - ALTER COLUMN uuid SET DEFAULT gen_random_uuid(); - -ALTER TABLE public.identities - ALTER COLUMN uuid SET NOT NULL; - -ALTER TABLE public.identities - ADD COLUMN IF NOT EXISTS user_uuid uuid; - -ALTER TABLE public.identities - ALTER COLUMN user_uuid TYPE uuid USING user_uuid::uuid; - -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_schema = 'public' - AND table_name = 'identities' - AND column_name = 'user_id' - ) THEN - UPDATE public.identities i - SET user_uuid = u.uuid - FROM public.users u - WHERE i.user_uuid IS NULL - AND u.id = i.user_id; - END IF; -END -$$; - -ALTER TABLE public.identities - ALTER COLUMN user_uuid SET NOT NULL; - -DO $$ -DECLARE - pk_name text; -BEGIN - SELECT conname INTO pk_name - FROM pg_constraint - WHERE conrelid = 'public.identities'::regclass - AND contype = 'p' - LIMIT 1; - - IF pk_name IS NULL THEN - ALTER TABLE public.identities - ADD CONSTRAINT identities_pkey PRIMARY KEY (uuid); - ELSIF pk_name <> 'identities_pkey' THEN - EXECUTE format('ALTER TABLE public.identities RENAME CONSTRAINT %I TO identities_pkey', pk_name); - END IF; -END -$$; - - -DO $$ -DECLARE - constraint_name text; - provider_att smallint; - external_att smallint; -BEGIN - SELECT attnum INTO provider_att - FROM pg_attribute - WHERE attrelid = 'public.identities'::regclass - AND attname = 'provider' - AND NOT attisdropped; - - SELECT attnum INTO external_att - FROM pg_attribute - WHERE attrelid = 'public.identities'::regclass - AND attname = 'external_id' - AND NOT attisdropped; - - IF provider_att IS NOT NULL AND external_att IS NOT NULL THEN - SELECT conname INTO constraint_name - FROM pg_constraint - WHERE conrelid = 'public.identities'::regclass - AND contype = 'u' - AND conkey = ARRAY[provider_att, external_att] - LIMIT 1; - - IF constraint_name IS NULL THEN - ALTER TABLE public.identities - ADD CONSTRAINT identities_provider_external_id_uk UNIQUE (provider, external_id); - ELSIF constraint_name <> 'identities_provider_external_id_uk' THEN - EXECUTE format('ALTER TABLE public.identities RENAME CONSTRAINT %I TO identities_provider_external_id_uk', constraint_name); - END IF; - END IF; -END -$$; - - -DO $$ -DECLARE - fk_name text; -BEGIN - SELECT conname INTO fk_name - FROM pg_constraint - WHERE conrelid = 'public.identities'::regclass - AND contype = 'f' - AND confrelid = 'public.users'::regclass - LIMIT 1; - - IF fk_name IS NULL THEN - ALTER TABLE public.identities - ADD CONSTRAINT identities_user_fk FOREIGN KEY (user_uuid) - REFERENCES public.users(uuid) ON DELETE CASCADE; - ELSIF fk_name <> 'identities_user_fk' THEN - EXECUTE format('ALTER TABLE public.identities RENAME CONSTRAINT %I TO identities_user_fk', fk_name); - END IF; -END -$$; - -DO $$ -DECLARE - idx_name text; - att smallint; -BEGIN - SELECT attnum INTO att - - FROM pg_attribute - WHERE attrelid = 'public.identities'::regclass - AND attname = 'user_uuid' - AND NOT attisdropped; - - - IF att IS NOT NULL THEN - - SELECT cls.relname INTO idx_name - FROM pg_index idx - JOIN pg_class cls ON cls.oid = idx.indexrelid - WHERE idx.indrelid = 'public.identities'::regclass - AND idx.indisunique = FALSE - AND idx.indkey = ARRAY[att]::int2vector - LIMIT 1; - - IF idx_name IS NULL THEN - CREATE INDEX IF NOT EXISTS idx_identities_user_uuid ON public.identities (user_uuid); - ELSIF idx_name <> 'idx_identities_user_uuid' THEN - EXECUTE format('ALTER INDEX %I RENAME TO idx_identities_user_uuid', idx_name); - END IF; - END IF; -END -$$; - -DO $$ -DECLARE - idx_name text; - att smallint; -BEGIN - SELECT attnum INTO att - - FROM pg_attribute - WHERE attrelid = 'public.identities'::regclass - AND attname = 'provider' - AND NOT attisdropped; - - - IF att IS NOT NULL THEN - SELECT cls.relname INTO idx_name - FROM pg_index idx - JOIN pg_class cls ON cls.oid = idx.indexrelid - WHERE idx.indrelid = 'public.identities'::regclass - AND idx.indisunique = FALSE - AND idx.indkey = ARRAY[att]::int2vector - - LIMIT 1; - - IF idx_name IS NULL THEN - CREATE INDEX IF NOT EXISTS idx_identities_provider ON public.identities (provider); - ELSIF idx_name <> 'idx_identities_provider' THEN - EXECUTE format('ALTER INDEX %I RENAME TO idx_identities_provider', idx_name); - END IF; - END IF; -END -$$; - --- SESSIONS ---------------------------------------------------------------- -ALTER TABLE public.sessions - ADD COLUMN IF NOT EXISTS uuid uuid; - -UPDATE public.sessions -SET uuid = gen_random_uuid() -WHERE uuid IS NULL; - -ALTER TABLE public.sessions - ALTER COLUMN uuid TYPE uuid USING uuid::uuid; - -ALTER TABLE public.sessions - ALTER COLUMN uuid SET DEFAULT gen_random_uuid(); - -ALTER TABLE public.sessions - ALTER COLUMN uuid SET NOT NULL; - -ALTER TABLE public.sessions - ADD COLUMN IF NOT EXISTS user_uuid uuid; - -ALTER TABLE public.sessions - ALTER COLUMN user_uuid TYPE uuid USING user_uuid::uuid; - -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_schema = 'public' - AND table_name = 'sessions' - AND column_name = 'user_id' - ) THEN - UPDATE public.sessions s - SET user_uuid = u.uuid - FROM public.users u - WHERE s.user_uuid IS NULL - AND u.id = s.user_id; - END IF; -END -$$; - -ALTER TABLE public.sessions - ALTER COLUMN user_uuid SET NOT NULL; - -DO $$ -DECLARE - pk_name text; -BEGIN - SELECT conname INTO pk_name - FROM pg_constraint - WHERE conrelid = 'public.sessions'::regclass - AND contype = 'p' - LIMIT 1; - - IF pk_name IS NULL THEN - ALTER TABLE public.sessions - ADD CONSTRAINT sessions_pkey PRIMARY KEY (uuid); - ELSIF pk_name <> 'sessions_pkey' THEN - EXECUTE format('ALTER TABLE public.sessions RENAME CONSTRAINT %I TO sessions_pkey', pk_name); - END IF; -END -$$; - -DO $$ -DECLARE - fk_name text; -BEGIN - SELECT conname INTO fk_name - FROM pg_constraint - WHERE conrelid = 'public.sessions'::regclass - AND contype = 'f' - AND confrelid = 'public.users'::regclass - LIMIT 1; - - IF fk_name IS NULL THEN - ALTER TABLE public.sessions - ADD CONSTRAINT sessions_user_fk FOREIGN KEY (user_uuid) - REFERENCES public.users(uuid) ON DELETE CASCADE; - ELSIF fk_name <> 'sessions_user_fk' THEN - EXECUTE format('ALTER TABLE public.sessions RENAME CONSTRAINT %I TO sessions_user_fk', fk_name); - END IF; -END -$$; - -DO $$ -DECLARE - idx_name text; - att smallint; -BEGIN - SELECT attnum INTO att - - FROM pg_attribute - WHERE attrelid = 'public.sessions'::regclass - AND attname = 'user_uuid' - AND NOT attisdropped; - - - IF att IS NOT NULL THEN - - SELECT cls.relname INTO idx_name - FROM pg_index idx - JOIN pg_class cls ON cls.oid = idx.indexrelid - WHERE idx.indrelid = 'public.sessions'::regclass - AND idx.indisunique = FALSE - AND idx.indkey = ARRAY[att]::int2vector - - LIMIT 1; - - IF idx_name IS NULL THEN - CREATE INDEX IF NOT EXISTS idx_sessions_user_uuid ON public.sessions (user_uuid); - ELSIF idx_name <> 'idx_sessions_user_uuid' THEN - EXECUTE format('ALTER INDEX %I RENAME TO idx_sessions_user_uuid', idx_name); - END IF; - END IF; -END -$$;