102 lines
3.0 KiB
Go
102 lines
3.0 KiB
Go
package database
|
|
|
|
import (
|
|
entity "RenewCMS/internal/infrastructure/persistence/models"
|
|
"fmt"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
const (
|
|
articleStoriesMarker = "article_stories"
|
|
articleStoryLanguageIndex = "idx_articles_story_lang"
|
|
articleStoryLanguageIndexSQL = "CREATE UNIQUE INDEX idx_articles_story_lang ON articles (story_id, lang, retired_id)"
|
|
articleStoryRetiredMarker = "article_story_language_retired"
|
|
articleRetiredIDsMarker = "article_retired_ids"
|
|
)
|
|
|
|
func BackfillArticleStories(db *gorm.DB) error {
|
|
return db.Transaction(func(tx *gorm.DB) error {
|
|
applied, err := markerApplied(tx, articleStoriesMarker)
|
|
if err != nil || applied {
|
|
return err
|
|
}
|
|
var ids []uint32
|
|
if err := tx.Unscoped().Model(&entity.Article{}).Where("story_id = ?", 0).Order("id ASC").Pluck("id", &ids).Error; err != nil {
|
|
return err
|
|
}
|
|
for _, id := range ids {
|
|
story := entity.Story{}
|
|
if err := tx.Create(&story).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Unscoped().Model(&entity.Article{}).Where("id = ?", id).UpdateColumn("story_id", story.ID).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return applyMarker(tx, articleStoriesMarker)
|
|
})
|
|
}
|
|
|
|
func BackfillRetiredArticles(db *gorm.DB) error {
|
|
return db.Transaction(func(tx *gorm.DB) error {
|
|
applied, err := markerApplied(tx, articleRetiredIDsMarker)
|
|
if err != nil || applied {
|
|
return err
|
|
}
|
|
if err := tx.Unscoped().Model(&entity.Article{}).Where("deleted_at IS NOT NULL AND retired_id = ?", 0).UpdateColumn("retired_id", gorm.Expr("id")).Error; err != nil {
|
|
return err
|
|
}
|
|
return applyMarker(tx, articleRetiredIDsMarker)
|
|
})
|
|
}
|
|
|
|
type duplicateStoryLanguage struct {
|
|
StoryID uint32
|
|
Lang string
|
|
RetiredID uint32
|
|
Versions int
|
|
}
|
|
|
|
func checkStoryLanguagesUnique(db *gorm.DB) error {
|
|
var duplicates []duplicateStoryLanguage
|
|
err := db.Unscoped().Model(&entity.Article{}).
|
|
Select("story_id, lang, retired_id, COUNT(*) AS versions").
|
|
Group("story_id, lang, retired_id").
|
|
Having("COUNT(*) > 1").
|
|
Order("story_id ASC, lang ASC").
|
|
Limit(1).
|
|
Scan(&duplicates).Error
|
|
if err != nil || len(duplicates) == 0 {
|
|
return err
|
|
}
|
|
first := duplicates[0]
|
|
return fmt.Errorf("cannot create the unique index %s: story %d holds %d versions in language %q; merge or delete the extra versions, then start again", articleStoryLanguageIndex, first.StoryID, first.Versions, first.Lang)
|
|
}
|
|
|
|
func ensureArticleStoryLanguageIndex(db *gorm.DB) error {
|
|
replaced, err := markerApplied(db, articleStoryRetiredMarker)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
indexed := db.Migrator().HasIndex(&entity.Article{}, articleStoryLanguageIndex)
|
|
if replaced && indexed {
|
|
return nil
|
|
}
|
|
if indexed {
|
|
if err := db.Migrator().DropIndex(&entity.Article{}, articleStoryLanguageIndex); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err := checkStoryLanguagesUnique(db); err != nil {
|
|
return err
|
|
}
|
|
if err := db.Exec(articleStoryLanguageIndexSQL).Error; err != nil {
|
|
return err
|
|
}
|
|
if replaced {
|
|
return nil
|
|
}
|
|
return applyMarker(db, articleStoryRetiredMarker)
|
|
}
|