From f9f57e95059d233f7d77de4d84e6ac3b86ec184d Mon Sep 17 00:00:00 2001 From: shaw Date: Mon, 13 Apr 2026 23:09:26 +0800 Subject: [PATCH] fix(migrations): add 097 to restore settings.updated_at default Legacy instances created the settings table via ent auto-migration, which emits Go-level defaults only. Migration 005 uses CREATE TABLE IF NOT EXISTS, so the missing SQL DEFAULT was never backfilled. This caused 098's raw INSERT to fail with a NOT NULL violation on updated_at. The new migration is idempotent and safe for fresh installs (no-op) and historical instances (backfills the default). --- .../097_fix_settings_updated_at_default.sql | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 backend/migrations/097_fix_settings_updated_at_default.sql diff --git a/backend/migrations/097_fix_settings_updated_at_default.sql b/backend/migrations/097_fix_settings_updated_at_default.sql new file mode 100644 index 00000000..e1d6f9b9 --- /dev/null +++ b/backend/migrations/097_fix_settings_updated_at_default.sql @@ -0,0 +1,27 @@ +-- 097_fix_settings_updated_at_default.sql +-- +-- 修复 settings.updated_at 列在历史实例上可能缺失 SQL DEFAULT 的问题。 +-- +-- 背景: +-- 早期版本曾依赖 ent 自动迁移建表(ent 的 Default(time.Now) 仅是 Go 层默认值, +-- 不会在 SQL 层落地为 DEFAULT),随后引入的 005_schema_parity.sql 使用了 +-- CREATE TABLE IF NOT EXISTS,对已存在的 settings 表不会重建,导致这部分实例 +-- 的 updated_at 列虽然是 NOT NULL,但缺少 SQL DEFAULT。 +-- +-- 后续 098_migrate_purchase_subscription_to_custom_menu.sql 是项目中唯一使用 +-- 原生 SQL INSERT INTO settings 的迁移(其余 settings 写入都走 ent / Go 层), +-- 因此该 schema 缺陷直到 098 才会触发: +-- "null value in column \"updated_at\" of relation \"settings\" violates not-null constraint" +-- +-- 幂等性: +-- - ALTER COLUMN ... SET DEFAULT NOW() 在已经具备相同默认值的实例上是无操作, +-- 不会报错(PostgreSQL 允许重复设置相同的默认值)。 +-- - UPDATE 子句的 WHERE updated_at IS NULL 在健康实例上匹配 0 行,不影响数据。 +-- +-- 这样可以同时兼容: +-- 1. 从未运行过旧版迁移的全新部署(005 已经把列建对,本迁移变成 no-op)。 +-- 2. 历史损坏实例(本迁移修复缺失的默认值,使后续 098 能够正常 INSERT)。 + +ALTER TABLE settings ALTER COLUMN updated_at SET DEFAULT NOW(); + +UPDATE settings SET updated_at = NOW() WHERE updated_at IS NULL;