Просмотр исходного кода

🐛 fix(db): allow re-adding models & vendors after soft delete; add safe index cleanup

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.
t0ng7u 6 месяцев назад
Родитель
Сommit
c776a1edff
1 измененных файлов с 20 добавлено и 9 удалено
  1. 20 9
      model/main.go

+ 20 - 9
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