Sync paired each declared index with the first existing index that had an equal definition and did not mark that index as taken. Two declared indexes with the same definition could therefore claim the same database index: the other one was dropped as unmatched and never recreated, or a missing one was never added. Claim an existing index with the same name and an equal definition first, then let the remaining declared indexes fall back to any unclaimed existing index with an equal definition, so renamed indexes are still kept as before. Iterate in name order so the result no longer depends on map order.
150 lines
4.7 KiB
Go
150 lines
4.7 KiB
Go
// Copyright 2026 The Xorm Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package xorm
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"xorm.io/xorm/schemas"
|
|
)
|
|
|
|
func newTestIndex(name string, indexType int, cols ...string) *schemas.Index {
|
|
index := schemas.NewIndex(name, indexType)
|
|
index.AddColumn(cols...)
|
|
return index
|
|
}
|
|
|
|
func indexMap(indexes ...*schemas.Index) map[string]*schemas.Index {
|
|
m := make(map[string]*schemas.Index, len(indexes))
|
|
for _, index := range indexes {
|
|
m[index.Name] = index
|
|
}
|
|
return m
|
|
}
|
|
|
|
func TestMatchIndexes(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
expected map[string]*schemas.Index
|
|
existing map[string]*schemas.Index
|
|
wantClaimed []string
|
|
wantMissing []string
|
|
}{
|
|
{
|
|
name: "same definition, both exist",
|
|
expected: indexMap(
|
|
newTestIndex("first", schemas.IndexType, "repo_id", "created_at"),
|
|
newTestIndex("second", schemas.IndexType, "repo_id", "created_at"),
|
|
),
|
|
existing: indexMap(
|
|
newTestIndex("first", schemas.IndexType, "repo_id", "created_at"),
|
|
newTestIndex("second", schemas.IndexType, "repo_id", "created_at"),
|
|
),
|
|
wantClaimed: []string{"first", "second"},
|
|
},
|
|
{
|
|
name: "same definition, first missing",
|
|
expected: indexMap(
|
|
newTestIndex("first", schemas.IndexType, "repo_id", "created_at"),
|
|
newTestIndex("second", schemas.IndexType, "repo_id", "created_at"),
|
|
),
|
|
existing: indexMap(
|
|
newTestIndex("second", schemas.IndexType, "repo_id", "created_at"),
|
|
),
|
|
wantClaimed: []string{"second"},
|
|
wantMissing: []string{"first"},
|
|
},
|
|
{
|
|
name: "same definition, second missing",
|
|
expected: indexMap(
|
|
newTestIndex("first", schemas.IndexType, "repo_id", "created_at"),
|
|
newTestIndex("second", schemas.IndexType, "repo_id", "created_at"),
|
|
),
|
|
existing: indexMap(
|
|
newTestIndex("first", schemas.IndexType, "repo_id", "created_at"),
|
|
),
|
|
wantClaimed: []string{"first"},
|
|
wantMissing: []string{"second"},
|
|
},
|
|
{
|
|
name: "same columns in different order",
|
|
expected: indexMap(
|
|
newTestIndex("c_u", schemas.IndexType, "user_id", "is_deleted", "created_unix"),
|
|
newTestIndex("c_u_d", schemas.IndexType, "created_unix", "user_id", "is_deleted"),
|
|
),
|
|
existing: indexMap(
|
|
newTestIndex("c_u_d", schemas.IndexType, "created_unix", "user_id", "is_deleted"),
|
|
),
|
|
wantClaimed: []string{"c_u_d"},
|
|
wantMissing: []string{"c_u"},
|
|
},
|
|
{
|
|
name: "renamed index keeps the existing one",
|
|
expected: indexMap(newTestIndex("new_name", schemas.IndexType, "repo_id")),
|
|
existing: indexMap(newTestIndex("old_name", schemas.IndexType, "repo_id")),
|
|
wantClaimed: []string{"old_name"},
|
|
},
|
|
{
|
|
name: "changed definition under the same name",
|
|
expected: indexMap(newTestIndex("repo", schemas.IndexType, "repo_id", "created_at")),
|
|
existing: indexMap(newTestIndex("repo", schemas.IndexType, "created_at", "repo_id")),
|
|
wantMissing: []string{"repo"},
|
|
},
|
|
{
|
|
name: "index turned into a unique",
|
|
expected: indexMap(newTestIndex("repo", schemas.UniqueType, "repo_id")),
|
|
existing: indexMap(newTestIndex("repo", schemas.IndexType, "repo_id")),
|
|
wantMissing: []string{"repo"},
|
|
},
|
|
{
|
|
name: "same name wins over a renamed duplicate",
|
|
expected: indexMap(
|
|
newTestIndex("first", schemas.IndexType, "repo_id"),
|
|
newTestIndex("second", schemas.IndexType, "repo_id"),
|
|
),
|
|
existing: indexMap(
|
|
newTestIndex("legacy", schemas.IndexType, "repo_id"),
|
|
newTestIndex("second", schemas.IndexType, "repo_id"),
|
|
),
|
|
wantClaimed: []string{"legacy", "second"},
|
|
},
|
|
{
|
|
name: "existing index is claimed only once",
|
|
expected: indexMap(
|
|
newTestIndex("a", schemas.IndexType, "repo_id"),
|
|
newTestIndex("b", schemas.IndexType, "repo_id"),
|
|
),
|
|
existing: indexMap(newTestIndex("legacy", schemas.IndexType, "repo_id")),
|
|
wantClaimed: []string{"legacy"},
|
|
wantMissing: []string{"b"},
|
|
},
|
|
{
|
|
name: "stale index is left unclaimed",
|
|
expected: indexMap(newTestIndex("repo", schemas.IndexType, "repo_id")),
|
|
existing: indexMap(
|
|
newTestIndex("repo", schemas.IndexType, "repo_id"),
|
|
newTestIndex("stale", schemas.IndexType, "owner_id"),
|
|
),
|
|
wantClaimed: []string{"repo"},
|
|
},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
for i := 0; i < 20; i++ {
|
|
claimed, missing := matchIndexes(tc.expected, tc.existing)
|
|
|
|
var claimedNames []string
|
|
for name, ok := range claimed {
|
|
if ok {
|
|
claimedNames = append(claimedNames, name)
|
|
}
|
|
}
|
|
assert.ElementsMatch(t, tc.wantClaimed, claimedNames)
|
|
assert.ElementsMatch(t, tc.wantMissing, sortedIndexNames(missing))
|
|
}
|
|
})
|
|
}
|
|
}
|