---
date: 2026-05-19
tags: [postgres, multi-schema, prisma, extension, citext, db-reset, dev-env]
severity: medium
status: workaround-known
---

# multi-schema 项目 drop schema cascade 误删 extension，后续 db:push 卡在 "type citext does not exist"

## 现象

slot-3 数据库 schema 已与 develop tip 漂移（v3 28-stage 重构改了 `ai_usage_events.total_tokens` 这种 generated column），`db:push:force` 卡在：

```
Error: ERROR: column "total_tokens" of relation "ai_usage_events" is a generated column
HINT: Use ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION instead.
```

`prisma migrate reset --force` 又因为 schema 残留报错：

```
A migration failed to apply.
Database error: ERROR: relation "flow_diagram" already exists
```

迫不得已手动 `DROP SCHEMA ... CASCADE` 所有 24 个业务 schema，重跑 `db:push:force` 又报：

```
Error: ERROR: type "citext" does not exist
```

## 根因

项目把 `citext` extension 装在 `platform_flow_diagram` schema 而不是 `public`（看 `pg_extension` 表能确认）。`DROP SCHEMA platform_flow_diagram CASCADE` 把 extension 一起 drop 了，后续表创建用到 `citext` 类型就找不到。

```
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to extension citext
        drop cascades to table _prisma_migrates
```

Prisma schema 用 `@db.Citext` 标注时，生成 DDL 不带 schema 前缀（只写 `citext`），所以 PG 走 `search_path` 找 — `search_path` 默认是 `"$user",public`，不在 `platform_flow_diagram` 上，找不到。

## 正确清理流程（multi-schema dev DB 重置）

```bash
PG_CONTAINER=ffws-wt-pg-<port>   # 例如 ffws-wt-pg-3702
DB=ffws_wt_slot_<N>

# 1. drop 全部业务 schema + recreate public
docker exec -i $PG_CONTAINER psql -U ffoa_dev -d $DB <<'SQL'
DO $$
DECLARE r RECORD;
BEGIN
  FOR r IN
    SELECT schema_name FROM information_schema.schemata
    WHERE schema_name NOT IN ('pg_catalog','information_schema','pg_toast','public')
      AND schema_name NOT LIKE 'pg_temp_%'
      AND schema_name NOT LIKE 'pg_toast_temp_%'
  LOOP
    EXECUTE format('DROP SCHEMA IF EXISTS %I CASCADE', r.schema_name);
  END LOOP;
END $$;
DROP SCHEMA IF EXISTS public CASCADE;
CREATE SCHEMA public;
SQL

# 2. 重装关键 extension 到 public（关键：装到 public 不是装回原来的业务 schema）
docker exec -i $PG_CONTAINER psql -U ffoa_dev -d $DB <<'SQL'
CREATE EXTENSION IF NOT EXISTS citext SCHEMA public;
SQL

# 3. push schema
cd backend && npm run db:push:force

# 4. seed + init itadmin
npm run db:seed
npm run init:itadmin
```

## 关键点

- `docker exec` 要加 `-i` 才能透传 stdin 给 psql；不带 `-i` 时 heredoc SQL 被吞掉，看着像执行了但其实啥也没做
- citext 装回时**用 public 不用原来 schema**——因为 prisma 生成 DDL 不带 schema 前缀
- 这个流程在每个 multi-schema slot 重置时都会撞，应该有个 helper 脚本

## 后续优化想法

- 写 `scripts/dev/reset-slot-db.sh <slot-N>` 封装上述 4 步，避免每次手敲
- 或者修 prisma schema 让 citext extension 声明显式指定 `schema = "public"`
- 或者在 `scripts/dev/agent-pool/pool-init.sh` 加 "post-reset hook" 自动重装 citext
