chore: remove legacy account migrations (#404)

This commit is contained in:
shenlan 2025-10-05 10:10:21 +08:00 committed by GitHub
parent 3de2dc0962
commit 149458d97f
6 changed files with 43 additions and 1100 deletions

View File

@ -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 <user> -d <dbname> > backup_before_uuid.sql
```
pg_dump -U <user> -d <dbname> > 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 <user> -d <dbname>`)并检查:
执行: psql -U <user> -d <dbname> -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 <user> -d <dbname>
5. **回滚策略**
若迁移出现问题,可使用备份恢复:
查看表结构:
```bash
psql -U <user> -d <dbname> < 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 <user> -d <dbname> < backup_before_uuid.sql
总结:
先启用扩展
再运行迁移脚本
验证新表结构和外键
保留备份,随时可回滚
> **小结**:新部署只需运行 `schema.sql`。旧环境若要沿用 UUID 主键改造,可参考上述思路自行调整脚本,并务必在操作前备份。

View File

@ -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 作为主键/外键
------------------------------------------------

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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
$$;