mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Fix non-ASCII search on database (#18437)
Use `ToASCIIUpper` for SQLite database on issues search, this because `UPPER(x)` on SQLite only transforms ASCII letters. Resolves #18429
This commit is contained in:
		@@ -23,6 +23,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/references"
 | 
						"code.gitea.io/gitea/modules/references"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/timeutil"
 | 
						"code.gitea.io/gitea/modules/timeutil"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
@@ -1862,7 +1863,12 @@ func GetRepoIssueStats(repoID, uid int64, filterMode int, isPull bool) (numOpen,
 | 
				
			|||||||
func SearchIssueIDsByKeyword(ctx context.Context, kw string, repoIDs []int64, limit, start int) (int64, []int64, error) {
 | 
					func SearchIssueIDsByKeyword(ctx context.Context, kw string, repoIDs []int64, limit, start int) (int64, []int64, error) {
 | 
				
			||||||
	repoCond := builder.In("repo_id", repoIDs)
 | 
						repoCond := builder.In("repo_id", repoIDs)
 | 
				
			||||||
	subQuery := builder.Select("id").From("issue").Where(repoCond)
 | 
						subQuery := builder.Select("id").From("issue").Where(repoCond)
 | 
				
			||||||
	kw = strings.ToUpper(kw)
 | 
						// SQLite's UPPER function only transforms ASCII letters.
 | 
				
			||||||
 | 
						if setting.Database.UseSQLite3 {
 | 
				
			||||||
 | 
							kw = util.ToUpperASCII(kw)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							kw = strings.ToUpper(kw)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	cond := builder.And(
 | 
						cond := builder.And(
 | 
				
			||||||
		repoCond,
 | 
							repoCond,
 | 
				
			||||||
		builder.Or(
 | 
							builder.Or(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -170,3 +170,14 @@ func CryptoRandomBytes(length int64) ([]byte, error) {
 | 
				
			|||||||
	_, err := rand.Read(buf)
 | 
						_, err := rand.Read(buf)
 | 
				
			||||||
	return buf, err
 | 
						return buf, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ToUpperASCII returns s with all ASCII letters mapped to their upper case.
 | 
				
			||||||
 | 
					func ToUpperASCII(s string) string {
 | 
				
			||||||
 | 
						b := []byte(s)
 | 
				
			||||||
 | 
						for i, c := range b {
 | 
				
			||||||
 | 
							if 'a' <= c && c <= 'z' {
 | 
				
			||||||
 | 
								b[i] -= 'a' - 'A'
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return string(b)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -186,3 +186,37 @@ func Test_OptionalBool(t *testing.T) {
 | 
				
			|||||||
	assert.Equal(t, OptionalBoolTrue, OptionalBoolParse("t"))
 | 
						assert.Equal(t, OptionalBoolTrue, OptionalBoolParse("t"))
 | 
				
			||||||
	assert.Equal(t, OptionalBoolTrue, OptionalBoolParse("True"))
 | 
						assert.Equal(t, OptionalBoolTrue, OptionalBoolParse("True"))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test case for any function which accepts and returns a single string.
 | 
				
			||||||
 | 
					type StringTest struct {
 | 
				
			||||||
 | 
						in, out string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var upperTests = []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"},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestToUpperASCII(t *testing.T) {
 | 
				
			||||||
 | 
						for _, tc := range upperTests {
 | 
				
			||||||
 | 
							assert.Equal(t, ToUpperASCII(tc.in), tc.out)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkToUpper(b *testing.B) {
 | 
				
			||||||
 | 
						for _, tc := range upperTests {
 | 
				
			||||||
 | 
							b.Run(tc.in, func(b *testing.B) {
 | 
				
			||||||
 | 
								for i := 0; i < b.N; i++ {
 | 
				
			||||||
 | 
									ToUpperASCII(tc.in)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user