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

Make table column branch.name case sensitive #28633

Closed
wants to merge 2 commits into from
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 docs/content/help/faq.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ the `utf8` charset, and connections which use the `utf8` charset will not use th

Please run `gitea doctor convert`, or run `ALTER DATABASE database_name CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;`
for the database_name and run `ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;`
for each table in the database.
for tables which are not using `utf8mb4` in the database.

## Why are Emoji displaying only as placeholders or in monochrome

Expand Down
4 changes: 1 addition & 3 deletions docs/content/help/faq.zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,9 +390,7 @@ SET GLOBAL innodb_large_prefix=1;
utf8 字符集的表和连接将不会使用它。

请运行 `gitea doctor convert` 或对数据库运行 `ALTER DATABASE database_name CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;`
并对每个表运行 `ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;`

您还需要将`app.ini`文件中的数据库字符集设置为`CHARSET=utf8mb4`
并对不是 `utf8mb4` 的表运行 `ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;`

## 为什么 Emoji 只显示占位符或单色图像

Expand Down
5 changes: 5 additions & 0 deletions models/db/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import (
"fmt"
"strconv"
"strings"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
Expand Down Expand Up @@ -34,6 +35,10 @@
return err
}

if strings.HasPrefix(table.Collation, "utf8mb4") {
fmt.Printf("skip table %q because it is already using utf8mb4\n", table.Name)

Check failure on line 39 in models/db/convert.go

View workflow job for this annotation

GitHub Actions / lint-backend

use of `fmt.Printf` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)

Check failure on line 39 in models/db/convert.go

View workflow job for this annotation

GitHub Actions / lint-go-windows

use of `fmt.Printf` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)

Check failure on line 39 in models/db/convert.go

View workflow job for this annotation

GitHub Actions / lint-go-gogit

use of `fmt.Printf` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)
continue
}
if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;", table.Name)); err != nil {
return err
}
Expand Down
24 changes: 22 additions & 2 deletions 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,20 @@ type Branch struct {
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}

// TableCollations is to make the "name" column case-sensitve for MySQL/MSSQL
func (b *Branch) TableCollations() []*schemas.Collation {
if setting.Database.Type.IsMySQL() {
return []*schemas.Collation{
{Column: "name", Name: "utf8mb4_bin"},
}
} else if setting.Database.Type.IsMSSQL() {
return []*schemas.Collation{
{Column: "name", Name: "Latin1_General_CS_AS"},
}
}
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 Expand Up @@ -380,7 +396,11 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
// except the indicate branch
func FindRecentlyPushedNewBranches(ctx context.Context, repoID, userID int64, excludeBranchName string) (BranchList, error) {
branches := make(BranchList, 0, 2)
subQuery := builder.Select("head_branch").From("pull_request").
sqlHeadBranch := "head_branch"
if setting.Database.Type.IsMSSQL() {
sqlHeadBranch = "CAST(head_branch AS nvarchar(255)) COLLATE Latin1_General_CS_AS" // FIXME: a dirty hack for MSSQL collation
}
subQuery := builder.Select(sqlHeadBranch).From("pull_request").
InnerJoin("issue", "issue.id = pull_request.issue_id").
Where(builder.Eq{
"pull_request.head_repo_id": repoID,
Expand Down
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,8 @@ var migrations = []Migration{
NewMigration("Add Index to pull_auto_merge.doer_id", v1_22.AddIndexToPullAutoMergeDoerID),
// v283 -> v284
NewMigration("Add combined Index to issue_user.uid and issue_id", v1_22.AddCombinedIndexToIssueUser),
// v284 -> v285
NewMigration("Alter branch name collation to be case-sensitive", v1_22.AlterBranchNameCollation),
}

// GetCurrentDBVersion returns the current db version
Expand Down
16 changes: 8 additions & 8 deletions models/migrations/v1_22/v283_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ import (
"testing"

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

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

func Test_AddCombinedIndexToIssueUser(t *testing.T) {
type IssueUser struct {
UID int64 `xorm:"INDEX unique(uid_to_issue)"` // User ID.
IssueID int64 `xorm:"INDEX unique(uid_to_issue)"`
ID int64 `xorm:"pk autoincr"`
UID int64 `xorm:"INDEX"` // User ID.
lunny marked this conversation as resolved.
Show resolved Hide resolved
IssueID int64 `xorm:"INDEX"`
IsRead bool
IsMentioned bool
}

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

if err := AddCombinedIndexToIssueUser(x); err != nil {
t.Fatal(err)
}
assert.NoError(t, AddCombinedIndexToIssueUser(x))
}
29 changes: 29 additions & 0 deletions models/migrations/v1_22/v284.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_22 //nolint

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

"xorm.io/xorm"
)

func AlterBranchNameCollation(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() {
if _, err := x.Exec("DROP INDEX UQE_branch_s ON branch"); err != nil {
log.Error("Failed to drop index UQE_branch_s on branch: %v", err) // ignore this error, in case the index has been dropped in previous migration
}
if _, err := x.Exec("ALTER TABLE branch ALTER COLUMN [name] nvarchar(255) COLLATE Latin1_General_CS_AS NOT NULL"); err != nil {
return err
}
if _, err := x.Exec("CREATE UNIQUE NONCLUSTERED INDEX UQE_branch_s ON branch (repo_id ASC, [name] ASC)"); err != nil {
return err
}
}
return nil
}
25 changes: 25 additions & 0 deletions models/migrations/v1_22/v284_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package v1_22 //nolint

import (
"testing"

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

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

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

x, deferable := base.PrepareTestEnv(t, 0, new(Branch))
defer deferable()

assert.NoError(t, AlterBranchNameCollation(x))
}
12 changes: 12 additions & 0 deletions tests/integration/api_branch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,18 @@ func testAPICreateBranches(t *testing.T, giteaURL *url.URL) {
NewBranch: "branch_2",
ExpectedHTTPStatus: http.StatusCreated,
},
// Trying to create a case-sensitive branch name
{
OldBranch: "new_branch_from_master_1",
NewBranch: "Branch_2",
ExpectedHTTPStatus: http.StatusCreated,
},
// Trying to create a branch with UTF8
{
OldBranch: "master",
NewBranch: "test-👀",
ExpectedHTTPStatus: http.StatusCreated,
},
// Trying to create from a branch which does not exist
{
OldBranch: "does_not_exist",
Expand Down
Loading