Bläddra i källkod

fix(database): improve MySQL Chinese character support validation

CaIon 6 månader sedan
förälder
incheckning
8db5356caf
1 ändrade filer med 55 tillägg och 12 borttagningar
  1. 55 12
      model/main.go

+ 55 - 12
model/main.go

@@ -389,12 +389,7 @@ func CloseDB() error {
 // default charset/collation can store Chinese characters. It allows common
 // Chinese-capable charsets (utf8mb4, utf8, gbk, big5, gb18030) and panics otherwise.
 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
 	var schemaCharset, schemaCollation string
@@ -416,20 +411,68 @@ func checkMySQLChineseSupport(db *gorm.DB) error {
 		csLower := toLower(cs)
 		clLower := toLower(cl)
 		if prefix, ok := allowedCharsets[csLower]; ok {
-			// collation should correspond to the charset when available
 			if clLower == "" {
 				return true
 			}
 			return strings.HasPrefix(clLower, prefix)
 		}
+		// 如果仅提供了排序规则,尝试按排序规则前缀判断
+		for _, prefix := range allowedCharsets {
+			if strings.HasPrefix(clLower, prefix) {
+				return true
+			}
+		}
 		return false
 	}
 
-	// We strictly require the CONNECTION and SCHEMA defaults to be Chinese-capable.
-	// We also check database/server variables and include them in the error for visibility.
-	if !isChineseCapable(charsetConn, collationConn) || !isChineseCapable(schemaCharset, schemaCollation) || !isChineseCapable(charsetDBVar, collationDBVar) {
-		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)",
-			charsetServer, collationServer, charsetDBVar, collationDBVar, charsetConn, collationConn, schemaCharset, schemaCollation)
+	// 1) 当前库默认值必须支持中文
+	if !isChineseCapable(schemaCharset, schemaCollation) {
+		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",
+			schemaCharset, schemaCollation, 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
 }