Fix tags table missing PRIMARY KEY causing duplicate tag display#504
Merged
Fix tags table missing PRIMARY KEY causing duplicate tag display#504
Conversation
The tags table was created by an older GORM version without a PRIMARY
KEY constraint on the name column. This allowed duplicate tag rows
(e.g. 14 rows for "android"), and GORM's Preload("Tags") JOIN returns
all matching rows, causing tags to appear duplicated in post views.
- Add fixTagsTable migration that detects the missing PK, deduplicates
rows with INSERT OR IGNORE INTO ... SELECT DISTINCT, and rebuilds
the table with a proper PRIMARY KEY
- Reload UpdatePost response into a fresh struct to prevent GORM from
appending preloaded tags to the existing slice
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR improves data integrity and API correctness around tags and post updates by adding a SQLite migration repair for legacy tags schemas and by preventing GORM from accumulating duplicated slice associations in the admin update response.
Changes:
- Add a SQLite-only migration step to rebuild
tagswithnameas a PRIMARY KEY and deduplicate existing rows when the table lacks a PK. - Update
Admin.UpdatePostto reload the updated post into a fresh struct before returning JSON (avoids GORM appending to existing slices).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
tools/migrate.go |
Adds fixTagsTable migration helper and invokes it during Migrate() to repair legacy tags schemas missing a primary key. |
admin/admin.go |
Reloads the updated post into a new struct before computing backlinks and returning the JSON response. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Distinguish sql.ErrNoRows from real errors in fixTagsTable Scan - Check error on post reload query in UpdatePost, return 500 on failure - Add TestMigrationFixesTagsWithoutPrimaryKey: creates legacy tags table without PK, inserts duplicates, runs Migrate, verifies deduplication, PK presence, and duplicate rejection Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Root cause: The
tagstable on staging was created by an older GORM migration without a PRIMARY KEY:This allowed duplicate rows (e.g. 14 rows for "android"). GORM's
Preload("Tags")JOINs throughpost_tags→tags, returning all matching tag rows per join entry, so posts displayed duplicated tags.Staging data: 610 total tag rows, only 545 unique — 65 duplicates.
Fixes
fixTagsTablemigration that detects the missing PK, deduplicates withINSERT OR IGNORE INTO tags_new SELECT DISTINCT name FROM tags, and rebuilds with a proper PRIMARY KEYUpdatePostresponse into a freshPoststruct to prevent GORM from appending preloaded tags to the existing sliceVerified against staging DB copy
Test plan
🤖 Generated with Claude Code