feat(db): add UUID migration script and update schema
This commit is contained in:
parent
af124368f0
commit
8b7e313521
68
account/sql/20251001-migrate_to_uuid.md
Normal file
68
account/sql/20251001-migrate_to_uuid.md
Normal 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
|
||||
|
||||
总结:
|
||||
|
||||
先启用扩展
|
||||
再运行迁移脚本
|
||||
验证新表结构和外键
|
||||
保留备份,随时可回滚
|
||||
117
account/sql/20251001-migrate_to_uuid.sql
Normal file
117
account/sql/20251001-migrate_to_uuid.sql
Normal 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 作为主键/外键
|
||||
------------------------------------------------
|
||||
@ -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
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user