fix: exact redirects with a trailing slash or an upper-case query fire at runtime (CR-03)

The resolver fetched exact rules with lower(source) IN keys, while the keys
drop one trailing slash and keep the query as written, so a source stored
as /old/ or /list?Page=2 was never read. A new migration adds the stored
generated column source_key (the SQL form of exactKey) with an index for
exact rules, and the fetch compares it.
This commit is contained in:
2026-10-27 15:20:19 +01:00
parent b19cd069a5
commit 2d81c261b5
3 changed files with 92 additions and 4 deletions

View File

@@ -0,0 +1,42 @@
package updates
import (
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)
// redirectSourceKey is the SQL form of classes.exactKey: the source's path
// lowercased without one trailing slash (the root stays "/"), then its
// query as written. The resolver fetches exact rules by this key, so a
// source stored as "/old/" or "/list?Page=2" is found for every request the
// in-memory matcher accepts.
const redirectSourceKey = `(CASE
WHEN length(split_part(source, '?', 1)) > 1 AND right(split_part(source, '?', 1), 1) = '/'
THEN lower(left(split_part(source, '?', 1), -1))
ELSE lower(split_part(source, '?', 1))
END) || (CASE WHEN strpos(source, '?') > 0 THEN substr(source, strpos(source, '?')) ELSE '' END)`
// addRedirectSourceKey adds golem15_seo_redirects.source_key, a stored
// generated column holding the exact-match lookup key, and indexes it for
// exact rules. Every write path fills it, because Postgres computes it.
var addRedirectSourceKey = []*gormigrate.Migration{
{
ID: "202610280001_add_golem15_seo_redirects_source_key",
Migrate: func(tx *gorm.DB) error {
if err := tx.Exec(`ALTER TABLE golem15_seo_redirects ADD COLUMN source_key TEXT GENERATED ALWAYS AS (` + redirectSourceKey + `) STORED`).Error; err != nil {
return err
}
return tx.Exec(`CREATE INDEX golem15_seo_redirects_source_key ON golem15_seo_redirects (source_key) WHERE match = 'exact'`).Error
},
Rollback: func(tx *gorm.DB) error {
if err := tx.Exec(`DROP INDEX IF EXISTS golem15_seo_redirects_source_key`).Error; err != nil {
return err
}
return tx.Exec(`ALTER TABLE golem15_seo_redirects DROP COLUMN IF EXISTS source_key`).Error
},
},
}
func init() {
Register(addRedirectSourceKey...)
}