mirror of
https://github.com/go-gitea/gitea.git
synced 2026-02-15 07:26:19 +00:00
feat(db): Improve BuildCaseInsensitiveLike with lowercase (#36598)
Improve BuildCaseInsensitiveLike with lowercase, users are more likely to input lowercase letters, so lowercase letters are used. --------- Signed-off-by: Tyrone Yeh <siryeh@gmail.com> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -12,30 +12,30 @@ import (
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// BuildCaseInsensitiveLike returns a condition to check if the given value is like the given key case-insensitively.
|
||||
// Handles especially SQLite correctly as UPPER there only transforms ASCII letters.
|
||||
// BuildCaseInsensitiveLike returns a case-insensitive LIKE condition for the given key and value.
|
||||
// Cast the search value and the database column value to the same case for case-insensitive matching.
|
||||
// * SQLite: only cast ASCII chars because it doesn't handle complete Unicode case folding
|
||||
// * Other databases: use database's string function, assuming that they are able to handle complete Unicode case folding correctly
|
||||
func BuildCaseInsensitiveLike(key, value string) builder.Cond {
|
||||
// ToLowerASCII is about 7% faster than ToUpperASCII (according to Golang's benchmark)
|
||||
if setting.Database.Type.IsSQLite3() {
|
||||
return builder.Like{"UPPER(" + key + ")", util.ToUpperASCII(value)}
|
||||
return builder.Like{"LOWER(" + key + ")", util.ToLowerASCII(value)}
|
||||
}
|
||||
return builder.Like{"UPPER(" + key + ")", strings.ToUpper(value)}
|
||||
return builder.Like{"LOWER(" + key + ")", strings.ToLower(value)}
|
||||
}
|
||||
|
||||
// BuildCaseInsensitiveIn returns a condition to check if the given value is in the given values case-insensitively.
|
||||
// Handles especially SQLite correctly as UPPER there only transforms ASCII letters.
|
||||
// See BuildCaseInsensitiveLike for more details
|
||||
func BuildCaseInsensitiveIn(key string, values []string) builder.Cond {
|
||||
uppers := make([]string, 0, len(values))
|
||||
incaseValues := make([]string, len(values))
|
||||
caseCast := strings.ToLower
|
||||
if setting.Database.Type.IsSQLite3() {
|
||||
for _, value := range values {
|
||||
uppers = append(uppers, util.ToUpperASCII(value))
|
||||
}
|
||||
} else {
|
||||
for _, value := range values {
|
||||
uppers = append(uppers, strings.ToUpper(value))
|
||||
}
|
||||
caseCast = util.ToLowerASCII
|
||||
}
|
||||
|
||||
return builder.In("UPPER("+key+")", uppers)
|
||||
for i, value := range values {
|
||||
incaseValues[i] = caseCast(value)
|
||||
}
|
||||
return builder.In("LOWER("+key+")", incaseValues)
|
||||
}
|
||||
|
||||
// BuilderDialect returns the xorm.Builder dialect of the engine
|
||||
|
||||
@@ -151,7 +151,7 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us
|
||||
func GetIssuePostersWithSearch(ctx context.Context, repo *Repository, isPull bool, search string, isShowFullName bool) ([]*user_model.User, error) {
|
||||
users := make([]*user_model.User, 0, 30)
|
||||
var prefixCond builder.Cond = builder.Like{"lower_name", strings.ToLower(search) + "%"}
|
||||
if isShowFullName {
|
||||
if search != "" && isShowFullName {
|
||||
prefixCond = prefixCond.Or(db.BuildCaseInsensitiveLike("full_name", "%"+search+"%"))
|
||||
}
|
||||
|
||||
|
||||
@@ -90,12 +90,12 @@ func CryptoRandomBytes(length int64) ([]byte, error) {
|
||||
return buf, err
|
||||
}
|
||||
|
||||
// ToUpperASCII returns s with all ASCII letters mapped to their upper case.
|
||||
func ToUpperASCII(s string) string {
|
||||
// ToLowerASCII returns s with all ASCII letters mapped to their lower case.
|
||||
func ToLowerASCII(s string) string {
|
||||
b := []byte(s)
|
||||
for i, c := range b {
|
||||
if 'a' <= c && c <= 'z' {
|
||||
b[i] -= 'a' - 'A'
|
||||
if 'A' <= c && c <= 'Z' {
|
||||
b[i] += 'a' - 'A'
|
||||
}
|
||||
}
|
||||
return string(b)
|
||||
|
||||
@@ -178,30 +178,26 @@ type StringTest struct {
|
||||
in, out string
|
||||
}
|
||||
|
||||
var upperTests = []StringTest{
|
||||
var lowerTests = []StringTest{
|
||||
{"", ""},
|
||||
{"ONLYUPPER", "ONLYUPPER"},
|
||||
{"abc", "ABC"},
|
||||
{"AbC123", "ABC123"},
|
||||
{"azAZ09_", "AZAZ09_"},
|
||||
{"longStrinGwitHmixofsmaLLandcAps", "LONGSTRINGWITHMIXOFSMALLANDCAPS"},
|
||||
{"long\u0250string\u0250with\u0250nonascii\u2C6Fchars", "LONG\u0250STRING\u0250WITH\u0250NONASCII\u2C6FCHARS"},
|
||||
{"\u0250\u0250\u0250\u0250\u0250", "\u0250\u0250\u0250\u0250\u0250"},
|
||||
{"a\u0080\U0010FFFF", "A\u0080\U0010FFFF"},
|
||||
{"lél", "LéL"},
|
||||
{"ABC", "abc"},
|
||||
{"AbC123_", "abc123_"},
|
||||
{"LONG\u0250string\u0250WITH\u0250non-ascii\u2C6FCHARS\u0080\uFFFF", "long\u0250string\u0250with\u0250non-ascii\u2C6Fchars\u0080\uFFFF"},
|
||||
{"lél", "lél"},
|
||||
{"LÉL", "lÉl"},
|
||||
}
|
||||
|
||||
func TestToUpperASCII(t *testing.T) {
|
||||
for _, tc := range upperTests {
|
||||
assert.Equal(t, ToUpperASCII(tc.in), tc.out)
|
||||
func TestToLowerASCII(t *testing.T) {
|
||||
for _, tc := range lowerTests {
|
||||
assert.Equal(t, ToLowerASCII(tc.in), tc.out)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkToUpper(b *testing.B) {
|
||||
for _, tc := range upperTests {
|
||||
func BenchmarkToLower(b *testing.B) {
|
||||
for _, tc := range lowerTests {
|
||||
b.Run(tc.in, func(b *testing.B) {
|
||||
for b.Loop() {
|
||||
ToUpperASCII(tc.in)
|
||||
ToLowerASCII(tc.in)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user