fix(database): improve MySQL Chinese character support validation

This commit is contained in:
CaIon
2025-08-12 16:31:00 +08:00
parent fa2edd9d3f
commit 8db5356caf

View File

@@ -389,12 +389,7 @@ func CloseDB() error {
// default charset/collation can store Chinese characters. It allows common // default charset/collation can store Chinese characters. It allows common
// Chinese-capable charsets (utf8mb4, utf8, gbk, big5, gb18030) and panics otherwise. // Chinese-capable charsets (utf8mb4, utf8, gbk, big5, gb18030) and panics otherwise.
func checkMySQLChineseSupport(db *gorm.DB) error { func checkMySQLChineseSupport(db *gorm.DB) error {
// Read session/server variables // 仅检测:当前库默认字符集/排序规则 + 各表的排序规则(隐含字符集)
var charsetServer, collationServer, charsetDBVar, collationDBVar, charsetConn, collationConn string
row := db.Raw("SELECT @@character_set_server, @@collation_server, @@character_set_database, @@collation_database, @@character_set_connection, @@collation_connection").Row()
if err := row.Scan(&charsetServer, &collationServer, &charsetDBVar, &collationDBVar, &charsetConn, &collationConn); err != nil {
return fmt.Errorf("读取 MySQL 字符集变量失败 / Failed to read MySQL charset variables: %v", err)
}
// Read current schema defaults // Read current schema defaults
var schemaCharset, schemaCollation string var schemaCharset, schemaCollation string
@@ -416,20 +411,68 @@ func checkMySQLChineseSupport(db *gorm.DB) error {
csLower := toLower(cs) csLower := toLower(cs)
clLower := toLower(cl) clLower := toLower(cl)
if prefix, ok := allowedCharsets[csLower]; ok { if prefix, ok := allowedCharsets[csLower]; ok {
// collation should correspond to the charset when available
if clLower == "" { if clLower == "" {
return true return true
} }
return strings.HasPrefix(clLower, prefix) return strings.HasPrefix(clLower, prefix)
} }
// 如果仅提供了排序规则,尝试按排序规则前缀判断
for _, prefix := range allowedCharsets {
if strings.HasPrefix(clLower, prefix) {
return true
}
}
return false return false
} }
// We strictly require the CONNECTION and SCHEMA defaults to be Chinese-capable. // 1) 当前库默认值必须支持中文
// We also check database/server variables and include them in the error for visibility. if !isChineseCapable(schemaCharset, schemaCollation) {
if !isChineseCapable(charsetConn, collationConn) || !isChineseCapable(schemaCharset, schemaCollation) || !isChineseCapable(charsetDBVar, collationDBVar) { return fmt.Errorf("当前库默认字符集/排序规则不支持中文schema(%s/%s)。请将库设置为 utf8mb4/utf8/gbk/big5/gb18030 / Schema default charset/collation is not Chinese-capable: schema(%s/%s). Please set to utf8mb4/utf8/gbk/big5/gb18030",
return fmt.Errorf("MySQL 字符集/排序规则必须支持中文(允许 utf8mb4/utf8/gbk/big5/gb18030请调整服务器、数据库或连接设置。/ MySQL charset/collation must be Chinese-capable (one of utf8mb4/utf8/gbk/big5/gb18030). Details: server(%s/%s), database_var(%s/%s), connection(%s/%s), schema(%s/%s)", schemaCharset, schemaCollation, schemaCharset, schemaCollation)
charsetServer, collationServer, charsetDBVar, collationDBVar, charsetConn, collationConn, schemaCharset, schemaCollation) }
// 2) 所有物理表的排序规则(隐含字符集)必须支持中文
type tableInfo struct {
Name string
Collation *string
}
var tables []tableInfo
if err := db.Raw("SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_TYPE = 'BASE TABLE'").Scan(&tables).Error; err != nil {
return fmt.Errorf("读取表排序规则失败 / Failed to read table collations: %v", err)
}
var badTables []string
for _, t := range tables {
// NULL 或空表示继承库默认设置,已在上面校验库默认,视为通过
if t.Collation == nil || *t.Collation == "" {
continue
}
cl := *t.Collation
// 仅凭排序规则判断是否中文可用
ok := false
lower := strings.ToLower(cl)
for _, prefix := range allowedCharsets {
if strings.HasPrefix(lower, prefix) {
ok = true
break
}
}
if !ok {
badTables = append(badTables, fmt.Sprintf("%s(%s)", t.Name, cl))
}
}
if len(badTables) > 0 {
// 限制输出数量以避免日志过长
maxShow := 20
shown := badTables
if len(shown) > maxShow {
shown = shown[:maxShow]
}
return fmt.Errorf(
"存在不支持中文的表,请修复其排序规则/字符集。示例(最多展示 %d 项):%v / Found tables not Chinese-capable. Please fix their collation/charset. Examples (showing up to %d): %v",
maxShow, shown, maxShow, shown,
)
} }
return nil return nil
} }