From e64b13c925b4a9c05849ab54f4794a39774294eb Mon Sep 17 00:00:00 2001 From: t0ng7u Date: Mon, 11 Aug 2025 01:25:13 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20chore(db):=20drop=20legacy=20sin?= =?UTF-8?q?gle-column=20UNIQUE=20indexes=20to=20prevent=20duplicate-key=20?= =?UTF-8?q?errors=20after=20soft-delete?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Why Previous versions created single-column UNIQUE constraints (`models.model_name`, `vendors.name`). After introducing composite indexes on `(model_name, deleted_at)` and `(name, deleted_at)` for soft-delete support, those legacy constraints could still exist in user databases. When a record was soft-deleted and re-inserted with the same name, MySQL raised `Error 1062 … for key 'models.model_name'`. What • In `migrateDB` and `migrateDBFast` paths of `model/main.go`, proactively drop: – `models.uk_model_name` and fallback `models.model_name` – `vendors.uk_vendor_name` and fallback `vendors.name` • Keeps existing helper `dropIndexIfExists` to ensure the operation is MySQL-only and error-free when indexes are already absent. Result Startup migration now removes every possible legacy UNIQUE index, ensuring composite index strategy works correctly. Users can soft-delete and recreate models/vendors with identical names without hitting duplicate-entry errors. --- model/main.go | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/model/main.go b/model/main.go index 49ccc56f..3c4931e2 100644 --- a/model/main.go +++ b/model/main.go @@ -66,18 +66,18 @@ var LOG_DB *gorm.DB // dropIndexIfExists drops a MySQL index only if it exists to avoid noisy 1091 errors func dropIndexIfExists(tableName string, indexName string) { - if !common.UsingMySQL { - return - } - var count int64 - // Check index existence via information_schema - err := DB.Raw( - "SELECT COUNT(1) FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = ? AND index_name = ?", - tableName, indexName, - ).Scan(&count).Error - if err == nil && count > 0 { - _ = DB.Exec("ALTER TABLE " + tableName + " DROP INDEX " + indexName + ";").Error - } + if !common.UsingMySQL { + return + } + var count int64 + // Check index existence via information_schema + err := DB.Raw( + "SELECT COUNT(1) FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = ? AND index_name = ?", + tableName, indexName, + ).Scan(&count).Error + if err == nil && count > 0 { + _ = DB.Exec("ALTER TABLE " + tableName + " DROP INDEX " + indexName + ";").Error + } } func createRootAccountIfNeed() error { @@ -252,8 +252,12 @@ func InitLogDB() (err error) { func migrateDB() error { // 修复旧版本留下的唯一索引,允许软删除后重新插入同名记录 - dropIndexIfExists("models", "uk_model_name") - dropIndexIfExists("vendors", "uk_vendor_name") + // 删除单列唯一索引(列级 UNIQUE)及早期命名方式,防止与新复合唯一索引 (model_name, deleted_at) 冲突 + dropIndexIfExists("models", "uk_model_name") // 新版复合索引名称(若已存在) + dropIndexIfExists("models", "model_name") // 旧版列级唯一索引名称 + + dropIndexIfExists("vendors", "uk_vendor_name") // 新版复合索引名称(若已存在) + dropIndexIfExists("vendors", "name") // 旧版列级唯一索引名称 if !common.UsingPostgreSQL { return migrateDBFast() } @@ -284,8 +288,12 @@ func migrateDB() error { func migrateDBFast() error { // 修复旧版本留下的唯一索引,允许软删除后重新插入同名记录 + // 删除单列唯一索引(列级 UNIQUE)及早期命名方式,防止与新复合唯一索引冲突 dropIndexIfExists("models", "uk_model_name") + dropIndexIfExists("models", "model_name") + dropIndexIfExists("vendors", "uk_vendor_name") + dropIndexIfExists("vendors", "name") var wg sync.WaitGroup @@ -305,7 +313,7 @@ func migrateDBFast() error { {&QuotaData{}, "QuotaData"}, {&Task{}, "Task"}, {&Model{}, "Model"}, - {&Vendor{}, "Vendor"}, + {&Vendor{}, "Vendor"}, {&PrefillGroup{}, "PrefillGroup"}, {&Setup{}, "Setup"}, {&TwoFA{}, "TwoFA"},