mirror of https://github.com/go-gitea/gitea.git
Add unique index for project_issue to prevent duplicate data (#30190)
Fix #27639
This commit is contained in:
parent
8a5c597c1d
commit
b482567059
|
@ -0,0 +1,9 @@
|
||||||
|
-
|
||||||
|
id: 1
|
||||||
|
project_id: 1
|
||||||
|
issue_id: 1
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 2
|
||||||
|
project_id: 1
|
||||||
|
issue_id: 1
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"code.gitea.io/gitea/models/migrations/v1_20"
|
"code.gitea.io/gitea/models/migrations/v1_20"
|
||||||
"code.gitea.io/gitea/models/migrations/v1_21"
|
"code.gitea.io/gitea/models/migrations/v1_21"
|
||||||
"code.gitea.io/gitea/models/migrations/v1_22"
|
"code.gitea.io/gitea/models/migrations/v1_22"
|
||||||
|
"code.gitea.io/gitea/models/migrations/v1_23"
|
||||||
"code.gitea.io/gitea/models/migrations/v1_6"
|
"code.gitea.io/gitea/models/migrations/v1_6"
|
||||||
"code.gitea.io/gitea/models/migrations/v1_7"
|
"code.gitea.io/gitea/models/migrations/v1_7"
|
||||||
"code.gitea.io/gitea/models/migrations/v1_8"
|
"code.gitea.io/gitea/models/migrations/v1_8"
|
||||||
|
@ -572,6 +573,10 @@ var migrations = []Migration{
|
||||||
NewMigration("Ensure every project has exactly one default column - No Op", noopMigration),
|
NewMigration("Ensure every project has exactly one default column - No Op", noopMigration),
|
||||||
// v293 -> v294
|
// v293 -> v294
|
||||||
NewMigration("Ensure every project has exactly one default column", v1_22.CheckProjectColumnsConsistency),
|
NewMigration("Ensure every project has exactly one default column", v1_22.CheckProjectColumnsConsistency),
|
||||||
|
|
||||||
|
// Gitea 1.22.0 ends at 294
|
||||||
|
|
||||||
|
NewMigration("Add unique index for project issue table", v1_23.AddUniqueIndexForProjectIssue),
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentDBVersion returns the current db version
|
// GetCurrentDBVersion returns the current db version
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_23 //nolint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/migrations/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
base.MainTest(m)
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_23 //nolint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"xorm.io/xorm"
|
||||||
|
"xorm.io/xorm/schemas"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddUniqueIndexForProjectIssue adds unique indexes for project issue table
|
||||||
|
func AddUniqueIndexForProjectIssue(x *xorm.Engine) error {
|
||||||
|
// remove possible duplicated records in table project_issue
|
||||||
|
type result struct {
|
||||||
|
IssueID int64
|
||||||
|
ProjectID int64
|
||||||
|
Cnt int
|
||||||
|
}
|
||||||
|
var results []result
|
||||||
|
if err := x.Select("issue_id, project_id, count(*) as cnt").
|
||||||
|
Table("project_issue").
|
||||||
|
GroupBy("issue_id, project_id").
|
||||||
|
Having("count(*) > 1").
|
||||||
|
Find(&results); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, r := range results {
|
||||||
|
if x.Dialect().URI().DBType == schemas.MSSQL {
|
||||||
|
if _, err := x.Exec(fmt.Sprintf("delete from project_issue where id in (SELECT top %d id FROM project_issue WHERE issue_id = ? and project_id = ?)", r.Cnt-1), r.IssueID, r.ProjectID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var ids []int64
|
||||||
|
if err := x.SQL("SELECT id FROM project_issue WHERE issue_id = ? and project_id = ? limit ?", r.IssueID, r.ProjectID, r.Cnt-1).Find(&ids); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := x.Table("project_issue").In("id", ids).Delete(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add unique index for project_issue table
|
||||||
|
type ProjectIssue struct { //revive:disable-line:exported
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
IssueID int64 `xorm:"INDEX unique(s)"`
|
||||||
|
ProjectID int64 `xorm:"INDEX unique(s)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.Sync(new(ProjectIssue))
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_23 //nolint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/migrations/base"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"xorm.io/xorm/schemas"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_AddUniqueIndexForProjectIssue(t *testing.T) {
|
||||||
|
type ProjectIssue struct { //revive:disable-line:exported
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
IssueID int64 `xorm:"INDEX"`
|
||||||
|
ProjectID int64 `xorm:"INDEX"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare and load the testing database
|
||||||
|
x, deferable := base.PrepareTestEnv(t, 0, new(ProjectIssue))
|
||||||
|
defer deferable()
|
||||||
|
if x == nil || t.Failed() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt, err := x.Table("project_issue").Where("project_id=1 AND issue_id=1").Count()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 2, cnt)
|
||||||
|
|
||||||
|
assert.NoError(t, AddUniqueIndexForProjectIssue(x))
|
||||||
|
|
||||||
|
cnt, err = x.Table("project_issue").Where("project_id=1 AND issue_id=1").Count()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 1, cnt)
|
||||||
|
|
||||||
|
tables, err := x.DBMetas()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 1, len(tables))
|
||||||
|
found := false
|
||||||
|
for _, index := range tables[0].Indexes {
|
||||||
|
if index.Type == schemas.UniqueType {
|
||||||
|
found = true
|
||||||
|
slices.Equal(index.Cols, []string{"project_id", "issue_id"})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.True(t, found)
|
||||||
|
}
|
Loading…
Reference in New Issue