Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert branch table name column to a new collation for mysql/mssql to support case sensitive because branch names are case sensitive #25623

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ require (
mvdan.cc/xurls/v2 v2.5.0
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
xorm.io/builder v0.3.12
xorm.io/xorm v1.3.3-0.20230623150031-18f8e7a86c75
xorm.io/xorm v1.3.3-0.20230701034009-d29fe4993351
)

require (
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1887,5 +1887,5 @@ strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/builder v0.3.12 h1:ASZYX7fQmy+o8UJdhlLHSW57JDOkM8DNhcAF5d0LiJM=
xorm.io/builder v0.3.12/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/xorm v1.3.3-0.20230623150031-18f8e7a86c75 h1:ReBAlO50dCIXCWF8Gbi0ZRa62AGAwCJNCPaUNUa7JSg=
xorm.io/xorm v1.3.3-0.20230623150031-18f8e7a86c75/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw=
xorm.io/xorm v1.3.3-0.20230701034009-d29fe4993351 h1:zjbf38FKz2hcqIspgMXYhwhGfy4iXZ/7YBqyv9ZNz8I=
xorm.io/xorm v1.3.3-0.20230701034009-d29fe4993351/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw=
20 changes: 19 additions & 1 deletion models/git/branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"

"xorm.io/builder"
"xorm.io/xorm/schemas"
)

// ErrBranchNotExist represents an error that branch with such name does not exist.
Expand Down Expand Up @@ -103,7 +105,7 @@ func (err ErrBranchesEqual) Unwrap() error {
type Branch struct {
ID int64
RepoID int64 `xorm:"UNIQUE(s)"`
Name string `xorm:"UNIQUE(s) NOT NULL"` // git's ref-name is case-sensitive internally, however, in some databases (mssql, mysql, by default), it's case-insensitive at the moment
Name string `xorm:"UNIQUE(s) NOT NULL"`
CommitID string
CommitMessage string `xorm:"TEXT"` // it only stores the message summary (the first line)
PusherID int64
Expand All @@ -117,6 +119,22 @@ type Branch struct {
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}

// TableCollations git's ref-name is case-sensitive internally, however, in some databases (mssql, mysql, by default), it's case-insensitive at the moment
// so we need to set the collation to case-sensitive which is utf8mb4_bin for mysql and Latin1_General_CS_AS for mssql
// the function is supported by xorm
func (b *Branch) TableCollations() []*schemas.Collation {
if setting.Database.Type.IsMySQL() {
return []*schemas.Collation{
{Name: "utf8mb4_bin", Column: "name"},
}
} else if setting.Database.Type.IsMSSQL() {
return []*schemas.Collation{
{Name: "Latin1_General_CS_AS", Column: "name"},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC SQLServer doesn't treat this collation as UTF-8 internally.

So it's better to add a test for it to make sure wide UTF-8 chars (eg: emoji) can be inserted and selected correctly.

}
}
return nil
}

func (b *Branch) LoadDeletedBy(ctx context.Context) (err error) {
if b.DeletedBy == nil {
b.DeletedBy, err = user_model.GetUserByID(ctx, b.DeletedByID)
Expand Down
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,8 @@ var migrations = []Migration{
NewMigration("Alter Actions Artifact table", v1_21.AlterActionArtifactTable),
// v266 -> v267
NewMigration("Reduce commit status", v1_21.ReduceCommitStatus),
// v267 -> v268
NewMigration("Change Branch name column collation to support case sensitive", v1_21.BranchColumnNameCollation),
}

// GetCurrentDBVersion returns the current db version
Expand Down
29 changes: 29 additions & 0 deletions models/migrations/v1_21/v265_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package v1_21 //nolint

import (
"testing"

"code.gitea.io/gitea/models/migrations/base"

"github.com/stretchr/testify/assert"
)

func Test_BranchColumnNameCollation(t *testing.T) {
type Branch struct {
ID int64
RepoID int64 `xorm:"Unique(s)"`
Name string `xorm:"Unique(s) NOT NULL"`
}

// Prepare and load the testing database
x, deferable := base.PrepareTestEnv(t, 0, new(Branch))
defer deferable()
if x == nil || t.Failed() {
return
}

assert.NoError(t, BranchColumnNameCollation(x))
}
21 changes: 21 additions & 0 deletions models/migrations/v1_21/v267.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package v1_21 //nolint

import (
"code.gitea.io/gitea/modules/setting"

"xorm.io/xorm"
)

func BranchColumnNameCollation(x *xorm.Engine) error {
if setting.Database.Type.IsMySQL() {
_, err := x.Exec("ALTER TABLE branch MODIFY COLUMN `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL")
return err
} else if setting.Database.Type.IsMSSQL() {
_, err := x.Exec("ALTER TABLE [branch] ALTER COLUMN [name] nvarchar(255) COLLATE Latin1_General_CS_AS NOT NULL;")
return err
}
return nil
}
18 changes: 18 additions & 0 deletions tests/integration/api_branch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,30 @@ func testAPICreateBranches(t *testing.T, giteaURL *url.URL) {
NewBranch: "branch_2",
ExpectedHTTPStatus: http.StatusCreated,
},
// Trying to create a Case insensive branch name
{
OldBranch: "new_branch_from_master_1",
NewBranch: "Branch_2",
ExpectedHTTPStatus: http.StatusCreated,
},
// Trying to create from a branch which does not exist
{
OldBranch: "does_not_exist",
NewBranch: "new_branch_from_non_existent",
ExpectedHTTPStatus: http.StatusNotFound,
},
// Trying to create a branch with utf8
{
OldBranch: "master",
NewBranch: "主版本",
ExpectedHTTPStatus: http.StatusCreated,
},
// Trying to create a branch with utf8
/*{
OldBranch: "master",
NewBranch: "My ❤️",
ExpectedHTTPStatus: http.StatusCreated,
},*/
}
for _, test := range testCases {
session := ctx.Session
Expand Down