From c776a1edff87bd5c9976d175f18eecbff91950ce Mon Sep 17 00:00:00 2001 From: t0ng7u Date: Sat, 9 Aug 2025 15:44:08 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(db):=20allow=20re-adding=20m?= =?UTF-8?q?odels=20&=20vendors=20after=20soft=20delete;=20add=20safe=20ind?= =?UTF-8?q?ex=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace legacy single-column unique indexes with composite unique indexes on (name, deleted_at) and introduce a safe index drop utility to eliminate duplicate-key errors and noisy MySQL 1091 warnings. WHAT • model/model_meta.go - Model.ModelName → `uniqueIndex:uk_model_name,priority:1` - Model.DeletedAt → `index; uniqueIndex:uk_model_name,priority:2` • model/vendor_meta.go - Vendor.Name → `uniqueIndex:uk_vendor_name,priority:1` - Vendor.DeletedAt → `index; uniqueIndex:uk_vendor_name,priority:2` • model/main.go - Add `dropIndexIfExists(table, index)`: • Checks `information_schema.statistics` • Drops index only when present (avoids Error 1091) - Invoke helper in `migrateDB` & `migrateDBFast` - Remove direct `ALTER TABLE … DROP INDEX …` calls WHY • Users received `Error 1062 (23000)` when re-creating a soft-deleted model/vendor because the old unique index enforced uniqueness on name alone. • Directly dropping nonexistent indexes caused MySQL `Error 1091` noise. HOW • Composite unique indexes `(model_name, deleted_at)` / `(name, deleted_at)` respect GORM soft deletes. • Safe helper ensures idempotent migrations across environments. RESULT • Users can now delete and re-add the same model or vendor without manual SQL. • Startup migration runs quietly across MySQL, PostgreSQL, and SQLite. • No behavior changes for existing data beyond index updates. TEST 1. Add model “deepseek-chat” → delete (soft) → re-add → success. 2. Add vendor “DeepSeek” → delete (soft) → re-add → success. 3. Restart service twice → no duplicate key or 1091 errors. --- model/main.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/model/main.go b/model/main.go index 08e3553a..49ccc56f 100644 --- a/model/main.go +++ b/model/main.go @@ -64,6 +64,22 @@ var DB *gorm.DB 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 + } +} + func createRootAccountIfNeed() error { var user User //if user.Status != common.UserStatusEnabled { @@ -236,11 +252,8 @@ func InitLogDB() (err error) { func migrateDB() error { // 修复旧版本留下的唯一索引,允许软删除后重新插入同名记录 - if common.UsingMySQL { - // 旧索引可能不存在,忽略删除错误即可 - _ = DB.Exec("ALTER TABLE models DROP INDEX uk_model_name;").Error - _ = DB.Exec("ALTER TABLE vendors DROP INDEX uk_vendor_name;").Error - } + dropIndexIfExists("models", "uk_model_name") + dropIndexIfExists("vendors", "uk_vendor_name") if !common.UsingPostgreSQL { return migrateDBFast() } @@ -271,10 +284,8 @@ func migrateDB() error { func migrateDBFast() error { // 修复旧版本留下的唯一索引,允许软删除后重新插入同名记录 - if common.UsingMySQL { - _ = DB.Exec("ALTER TABLE models DROP INDEX uk_model_name;").Error - _ = DB.Exec("ALTER TABLE vendors DROP INDEX uk_vendor_name;").Error - } + dropIndexIfExists("models", "uk_model_name") + dropIndexIfExists("vendors", "uk_vendor_name") var wg sync.WaitGroup