fix(db): keep same-column indexes in different order on sync

xorm pairs declared and existing indexes by column set, ignoring name
and column order, so on every sync the `action` table could drop one of
`c_u` and `c_u_d`, and never recreate a missing one. Tables with such
index pairs are now synced with IgnoreIndices and their plain indexes are
reconciled by name.

Fix https://github.com/go-gitea/gitea/issues/39244
This commit is contained in:
dualfroz committed 2026-09-29 11:39:12 +02:00
1 parent 20681b2034
commit ebd99f1d95
2 files changed
+121 -4

No files matched your search

+42
View File
@@ -7,6 +7,7 @@ import (
"path/filepath"
"testing"
activities_model "gitea.dev/models/activities"
"gitea.dev/models/db"
issues_model "gitea.dev/models/issues"
"gitea.dev/models/unittest"
@@ -16,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"xorm.io/xorm/schemas"
)
func TestDumpDatabase(t *testing.T) {
@@ -34,6 +36,46 @@ func TestDumpDatabase(t *testing.T) {
}
}
func TestSyncAllTablesKeepsSameColumnIndexes(t *testing.T) {
// action declares c_u and c_u_d with the same columns in different order
require.NoError(t, unittest.PrepareTestDatabase())
x := db.GetXORMEngineForTesting()
table, err := x.TableInfo(new(activities_model.Action))
require.NoError(t, err)
declared := map[string][]string{}
for name, index := range table.Indexes {
declared[name] = index.Cols
}
require.Contains(t, declared, "c_u")
require.Contains(t, declared, "c_u_d")
existing := func() (map[string][]string, map[string]*schemas.Index) {
indexes, err := x.Dialect().GetIndexes(x.DB(), t.Context(), table.Name)
require.NoError(t, err)
cols := map[string][]string{}
for name, index := range indexes {
cols[name] = index.Cols
}
return cols, indexes
}
for range 10 {
require.NoError(t, db.SyncAllTables())
cols, _ := existing()
require.Equal(t, declared, cols)
}
for _, name := range []string{"c_u", "c_u_d"} {
_, indexes := existing()
_, err = x.Exec(x.Dialect().DropIndexSQL(table.Name, indexes[name]))
require.NoError(t, err)
require.NoError(t, db.SyncAllTables())
cols, _ := existing()
assert.Equal(t, declared, cols, "index %s should be restored", name)
}
}
func TestDeleteOrphanedObjects(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())