FR-035 — SSP Table Rename 設計 / 影響分析

類型:Refactor(DB table rename,跨 BE + jedi-oscal + 4 DB 環境) 提出:2026-06-10 | Branch:feature/ssp-table-rename

§1

需求(白話)

最早把完整的 system_security_plan 當 table 前綴命名,太冗長。簡化為 ssp,跟既有的 ssp_components / ssp_inventory_items 等子表命名對齊。三張要改:

現名 新名
oscal.system_security_plans oscal.ssps
oscal.system_security_plan_control_implementations oscal.ssp_control_implementations
oscal.system_security_plans_system_characteristics oscal.ssp_system_characteristics
§2

決策(user 拍板 2026-06-10)

  1. 核心表用 ssps(複數) — 與既有 ssp_* 子表複數慣例一致(system_security_plans 本就複數)。
  2. 改 jedi-oscal 套件、走 dev path dependency — ORM 定義在套件內;dev 階段 pyproject 切 path,feature 完成才發版推 Nexus。
  3. index / constraint / sequence / pkey 舊名一併 rename 乾淨 — 不只 ALTER TABLE
§3

Scope 邊界

  • 表名 only,欄位不動system_security_plan_idssp_id 等欄位維持原樣(避免爆炸性 blast radius)。
  • ssp_ 前綴的子表(components / inventory_items / leveraged_authorizations / control_implementation_objectives / docx_parse_jobs / reference_documents…)不改
  • FE / E2E test repo:grep 確認表名引用,不動。
  • API field / JSON key(sspUid 等)非 DB 表名,不在範圍
§4

影響盤點(grep 4 repo + live DB introspection)

區域 內容
jedi-oscal(8 檔) 3 個 __tablename__ + 6 個 ix_system_security_plans_* index + 8 條 schema-qualified FK 字串 + docstring。其他 jedi-* 套件零引用。
BE 主專案 FK 字串 project_system_characteristic.py;raw SQL module_frame_template_copy_service.py(4 條)+ smoke_test_ssp_docx_parser.py(2 條);測試斷言 3 檔;doc-gen / markdown(留收尾批次)。
FE / test 零。
live DB(DEV introspection) 無 view、無 function/stored procedure 依賴這三表(消除整類風險);8 條 incoming FK + 3 條 outgoing FK(ALTER RENAME 由 OID 自動跟著走,不斷);6 個 index + 3 個 sequence + pkey/uk 舊名殘留待 rename。
§5

技術做法

  • in-place ALTER TABLE ... RENAME(非 drop/recreate):資料保留、FK 由 OID 自動跟隨。DEV 有 2,375+ 筆資料,零丟失。
  • Lockstep 上版:DB rename + jedi-oscal 新 ORM + app 重啟必須同步 —— 舊 ORM 對改名表、或新 ORM 對舊表都會炸(踩過 feedback_cross_schema_fk_must_qualify)。
  • Migrationscripts/sql/2026-06-10-ssp-table-rename.sql,可攜,psql --single-transaction -v ON_ERROR_STOP=1cmmgr 套;尾段 INSERT public.schema_migrations。表權限(GRANT cm_app)隨 table object 跟隨,不需 re-GRANT。
§6

風險與緩解

風險 緩解
舊 app 撞改名表 → 500 / NoReferencedTableError Lockstep:migration + 新 ORM 部署成對,每環境一起上
hardcode 的 constraint 名各環境不同 套用前對 catalog 重新驗名(DEV 已驗,名稱 1:1 符合)
grep 看不到的 raw SQL 漏改 grep gate(api/app/domain/infra)+ live API e2e smoke
誤改欄位 scope 邊界明列欄位排除;diff 驗 - 行無 system_security_plan_id
§7

驗證結果(DEV,2026-06-10)

  • BEGIN/ROLLBACK 乾跑零錯 → 真實套用 DEV:9 條 FK 全 valid、資料保留(219/2375/200)。
  • BE boot 無 mapper 錯誤;live API GET /ssp/<uid>/control-implementations 回真實資料、log 零 SQL 錯誤。
  • pytest baseline diff:零新回歸(4 個既有 fail 與本次無關)。
§8

狀態

  • jedi-oscal ORM:commit 278bfbb(monorepo,未 push、未發版)。
  • BE 程式 + migration + 測試:commit 3ca0ad9d(未 push)。
  • 待辦:STG/POC/PROD 套 migration(user-gated)→ jedi-oscal 發版 Nexus + BE 還原 pin(user-gated)→ doc/changelog 收尾。

實作步驟逐項見 implementation-plan.md