summaryrefslogtreecommitdiffstats
path: root/internal/db/posts.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/db/posts.go')
-rw-r--r--internal/db/posts.go52
1 files changed, 52 insertions, 0 deletions
diff --git a/internal/db/posts.go b/internal/db/posts.go
index c1360ab..ab231db 100644
--- a/internal/db/posts.go
+++ b/internal/db/posts.go
@@ -200,3 +200,55 @@ func (m *MetaDB) RenamePost(oldSlug, newSlug string) error {
return tx.Commit()
}
+
+// RenameAndUpsertPost atomically renames a post from oldSlug to p.Slug and
+// updates its content, collapsing any redirect chains and adding a redirect
+// from oldSlug to p.Slug — all in a single transaction.
+func (m *MetaDB) RenameAndUpsertPost(oldSlug string, p PostRecord) error {
+ if oldSlug == p.Slug {
+ return m.UpsertPost(p)
+ }
+ tx, err := m.db.Begin()
+ if err != nil {
+ return err
+ }
+ defer tx.Rollback()
+
+ tags, _ := json.Marshal(p.Tags)
+ draftInt := 0
+ if p.Draft {
+ draftInt = 1
+ }
+
+ // Insert new record with updated content
+ if _, err = tx.Exec(`
+ INSERT INTO posts (slug, title, date, tags, draft, blocks, updated_at)
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
+ p.Slug, p.Title, p.Date, string(tags), draftInt, p.Blocks, p.UpdatedAt,
+ ); err != nil {
+ return err
+ }
+
+ // Delete old record
+ if _, err = tx.Exec(`DELETE FROM posts WHERE slug = ?`, oldSlug); err != nil {
+ return err
+ }
+
+ // Collapse existing redirect chains
+ if _, err = tx.Exec(
+ `UPDATE redirects SET to_slug = ? WHERE to_slug = ?`, p.Slug, oldSlug,
+ ); err != nil {
+ return err
+ }
+
+ // Add redirect from oldSlug to new slug
+ if _, err = tx.Exec(`
+ INSERT INTO redirects (from_slug, to_slug) VALUES (?, ?)
+ ON CONFLICT(from_slug) DO UPDATE SET to_slug = excluded.to_slug`,
+ oldSlug, p.Slug,
+ ); err != nil {
+ return err
+ }
+
+ return tx.Commit()
+}