feat(db): add UUID migration script and update schema

This commit is contained in:
Haitao Pan 2025-10-01 17:27:47 +08:00
parent af124368f0
commit 8b7e313521
3 changed files with 197 additions and 5 deletions

View File

@ -0,0 +1,68 @@
# 前置准备
1. 确认扩展
登录 PostgreSQL执行 \dx 看看是否已经有 uuid-ossp 或 pgcrypto。
如果没有,就执行:
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
或者:
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
⚠️ 推荐用 pgcrypto函数是 gen_random_uuid()),更现代;
如果你已经用了 uuid-ossp脚本里的 uuid_generate_v4() 就能直接用。
2. 备份数据库
这是最关键的保险:
pg_dump -U <user> -d <dbname> > backup_before_uuid.sql
3. 执行迁移
假设你的 schema 在 schema.sql 文件里,迁移脚本在 migrate_to_uuid.sql 文件里。
执行: psql -U <user> -d <dbname> -f migrate_to_uuid.sql
这个脚本会做三步:
- 新增 uuid 列,填充数据。
- 新建 user_uuid 外键字段,保持和旧 id 外键并存。
- 删除旧的 id、user_id把 uuid 设为主键。
4. 验证结果
迁移后,进入数据库: psql -U <user> -d <dbname>
查看表结构:
\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
总结:
先启用扩展
再运行迁移脚本
验证新表结构和外键
保留备份,随时可回滚

View File

@ -0,0 +1,117 @@
------------------------------------------------
-- 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;
ALTER TABLE users
ADD CONSTRAINT users_uuid_unique UNIQUE (uuid);
-- ========== 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;
ALTER TABLE identities
ADD CONSTRAINT identities_uuid_unique UNIQUE (uuid);
-- 新增 user_uuid 外键字段
ALTER TABLE identities
ADD COLUMN IF NOT EXISTS user_uuid UUID;
UPDATE identities i
SET user_uuid = u.uuid
FROM users u
WHERE i.user_id = u.id;
ALTER TABLE identities
ALTER COLUMN user_uuid SET NOT NULL;
ALTER TABLE identities
ADD CONSTRAINT identities_user_uuid_fk FOREIGN KEY (user_uuid) REFERENCES users(uuid) ON DELETE CASCADE;
-- ========== 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;
ALTER TABLE sessions
ADD CONSTRAINT sessions_uuid_unique UNIQUE (uuid);
-- 新增 user_uuid 外键字段
ALTER TABLE sessions
ADD COLUMN IF NOT EXISTS user_uuid UUID;
UPDATE sessions s
SET user_uuid = u.uuid
FROM users u
WHERE s.user_id = u.id;
ALTER TABLE sessions
ALTER COLUMN user_uuid SET NOT NULL;
ALTER TABLE sessions
ADD CONSTRAINT sessions_user_uuid_fk FOREIGN KEY (user_uuid) REFERENCES users(uuid) ON DELETE CASCADE;
------------------------------------------------
-- STEP 2: 清理(彻底切换到 UUID 主键)
------------------------------------------------
-- 删除原有的主键约束
ALTER TABLE users DROP CONSTRAINT users_pkey;
ALTER TABLE identities DROP CONSTRAINT identities_pkey;
ALTER TABLE sessions DROP CONSTRAINT 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 为新的主键
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 id;
ALTER TABLE identities DROP COLUMN id;
ALTER TABLE sessions DROP COLUMN id;
-- 删除旧的 user_id 外键字段
ALTER TABLE identities DROP COLUMN user_id;
ALTER TABLE sessions DROP COLUMN user_id;
------------------------------------------------
-- Done: 所有表都只用 UUID 作为主键/外键
------------------------------------------------

View File

@ -1,5 +1,10 @@
-- 启用扩展(只需执行一次)
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- 或者CREATE EXTENSION IF NOT EXISTS "pgcrypto";
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
id SERIAL UNIQUE, -- 保留自增 id 作为内部用途
uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), -- 业务主键
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
email TEXT,
@ -7,16 +12,18 @@ CREATE TABLE IF NOT EXISTS users (
);
CREATE TABLE IF NOT EXISTS identities (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
id SERIAL UNIQUE,
uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(uuid) ON DELETE CASCADE,
provider TEXT NOT NULL,
external_id TEXT NOT NULL,
UNIQUE(provider, external_id)
);
CREATE TABLE IF NOT EXISTS sessions (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
id SERIAL UNIQUE,
uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(uuid) ON DELETE CASCADE,
token TEXT NOT NULL,
expires_at TIMESTAMPTZ NOT NULL
);