package db import ( "regexp" "strings" ) // IdentifierProcessor 标识符处理器 // 用于处理表名、字段名的前缀添加和引号转换 type IdentifierProcessor struct { dialect Dialect prefix string } // NewIdentifierProcessor 创建标识符处理器 func NewIdentifierProcessor(dialect Dialect, prefix string) *IdentifierProcessor { return &IdentifierProcessor{ dialect: dialect, prefix: prefix, } } // ProcessTableName 处理表名(添加前缀+引号) // 输入: "order" 或 "`order`" 或 "\"order\"" // 输出: "`app_order`" (MySQL) 或 "\"app_order\"" (PostgreSQL/SQLite) func (p *IdentifierProcessor) ProcessTableName(name string) string { // 去除已有的引号 name = p.stripQuotes(name) // 检查是否包含空格(别名情况,如 "order AS o") if strings.Contains(name, " ") { // 处理别名情况 parts := strings.SplitN(name, " ", 2) tableName := p.stripQuotes(parts[0]) alias := parts[1] return p.dialect.QuoteIdentifier(p.prefix+tableName) + " " + alias } // 添加前缀和引号 return p.dialect.QuoteIdentifier(p.prefix + name) } // ProcessTableNameNoPrefix 处理表名(只添加引号,不添加前缀) // 用于已经包含前缀的情况 func (p *IdentifierProcessor) ProcessTableNameNoPrefix(name string) string { name = p.stripQuotes(name) if strings.Contains(name, " ") { parts := strings.SplitN(name, " ", 2) tableName := p.stripQuotes(parts[0]) alias := parts[1] return p.dialect.QuoteIdentifier(tableName) + " " + alias } return p.dialect.QuoteIdentifier(name) } // ProcessColumn 处理 table.column 格式 // 输入: "name" 或 "order.name" 或 "`order`.name" 或 "`order`.`name`" // 输出: "`name`" 或 "`app_order`.`name`" (MySQL) func (p *IdentifierProcessor) ProcessColumn(name string) string { // 检查是否包含点号 if !strings.Contains(name, ".") { // 单独的列名,只加引号 return p.dialect.QuoteIdentifier(p.stripQuotes(name)) } // 处理 table.column 格式 parts := p.splitTableColumn(name) if len(parts) == 2 { tableName := p.stripQuotes(parts[0]) columnName := p.stripQuotes(parts[1]) // 表名添加前缀 return p.dialect.QuoteIdentifier(p.prefix+tableName) + "." + p.dialect.QuoteIdentifier(columnName) } // 无法解析,返回原样但转换引号 return p.convertQuotes(name) } // ProcessColumnNoPrefix 处理 table.column 格式(不添加前缀) func (p *IdentifierProcessor) ProcessColumnNoPrefix(name string) string { if !strings.Contains(name, ".") { return p.dialect.QuoteIdentifier(p.stripQuotes(name)) } parts := p.splitTableColumn(name) if len(parts) == 2 { tableName := p.stripQuotes(parts[0]) columnName := p.stripQuotes(parts[1]) return p.dialect.QuoteIdentifier(tableName) + "." + p.dialect.QuoteIdentifier(columnName) } return p.convertQuotes(name) } // ProcessConditionString 智能解析条件字符串(如 ON 条件) // 输入: "user.id = order.user_id AND order.status = 1" // 输出: "`app_user`.`id` = `app_order`.`user_id` AND `app_order`.`status` = 1" (MySQL) func (p *IdentifierProcessor) ProcessConditionString(condition string) string { if condition == "" { return condition } result := condition // 首先处理已有完整引号的情况 `table`.`column` 或 "table"."column" // 这些需要先处理,因为它们的格式最明确 fullyQuotedPattern := regexp.MustCompile("[`\"]([a-zA-Z_][a-zA-Z0-9_]*)[`\"]\\.[`\"]([a-zA-Z_][a-zA-Z0-9_]*)[`\"]") result = fullyQuotedPattern.ReplaceAllStringFunc(result, func(match string) string { parts := fullyQuotedPattern.FindStringSubmatch(match) if len(parts) == 3 { tableName := parts[1] colName := parts[2] return p.dialect.QuoteIdentifier(p.prefix+tableName) + "." + p.dialect.QuoteIdentifier(colName) } return match }) // 然后处理部分引号的情况 `table`.column 或 "table".column // 注意:需要避免匹配已处理的内容(已经是双引号包裹的) quotedTablePattern := regexp.MustCompile("[`\"]([a-zA-Z_][a-zA-Z0-9_]*)[`\"]\\.([a-zA-Z_][a-zA-Z0-9_]*)(?:[^`\"]|$)") result = quotedTablePattern.ReplaceAllStringFunc(result, func(match string) string { parts := quotedTablePattern.FindStringSubmatch(match) if len(parts) >= 3 { tableName := parts[1] colName := parts[2] // 保留末尾字符(如果有) suffix := "" if len(match) > len(parts[0])-1 { lastChar := match[len(match)-1] if lastChar != '`' && lastChar != '"' && !isIdentChar(lastChar) { suffix = string(lastChar) } } return p.dialect.QuoteIdentifier(p.prefix+tableName) + "." + p.dialect.QuoteIdentifier(colName) + suffix } return match }) // 最后处理无引号的情况 table.column // 使用更精确的正则,确保不匹配已处理的内容 unquotedPattern := regexp.MustCompile(`([^` + "`" + `"\w]|^)([a-zA-Z_][a-zA-Z0-9_]*)\.([a-zA-Z_][a-zA-Z0-9_]*)([^` + "`" + `"\w(]|$)`) result = unquotedPattern.ReplaceAllStringFunc(result, func(match string) string { parts := unquotedPattern.FindStringSubmatch(match) if len(parts) >= 5 { prefix := parts[1] // 前面的边界字符 tableName := parts[2] colName := parts[3] suffix := parts[4] // 后面的边界字符 return prefix + p.dialect.QuoteIdentifier(p.prefix+tableName) + "." + p.dialect.QuoteIdentifier(colName) + suffix } return match }) return result } // isIdentChar 判断是否是标识符字符 func isIdentChar(c byte) bool { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' } // ProcessFieldList 处理字段列表字符串 // 输入: "order.id, user.name AS uname, COUNT(*)" // 输出: "`app_order`.`id`, `app_user`.`name` AS uname, COUNT(*)" (MySQL) func (p *IdentifierProcessor) ProcessFieldList(fields string) string { if fields == "" || fields == "*" { return fields } // 使用与 ProcessConditionString 相同的逻辑 return p.ProcessConditionString(fields) } // stripQuotes 去除标识符两端的引号(反引号或双引号) func (p *IdentifierProcessor) stripQuotes(name string) string { name = strings.TrimSpace(name) // 去除反引号 if strings.HasPrefix(name, "`") && strings.HasSuffix(name, "`") { return name[1 : len(name)-1] } // 去除双引号 if strings.HasPrefix(name, "\"") && strings.HasSuffix(name, "\"") { return name[1 : len(name)-1] } return name } // splitTableColumn 分割 table.column 格式 // 支持: table.column, `table`.column, `table`.`column`, "table".column 等 func (p *IdentifierProcessor) splitTableColumn(name string) []string { // 先尝试按点号分割 dotIndex := -1 // 查找不在引号内的点号 inQuote := false quoteChar := byte(0) for i := 0; i < len(name); i++ { c := name[i] if c == '`' || c == '"' { if !inQuote { inQuote = true quoteChar = c } else if c == quoteChar { inQuote = false } } else if c == '.' && !inQuote { dotIndex = i break } } if dotIndex == -1 { return []string{name} } return []string{name[:dotIndex], name[dotIndex+1:]} } // convertQuotes 将已有的引号转换为当前方言的引号格式 func (p *IdentifierProcessor) convertQuotes(name string) string { quoteChar := p.dialect.QuoteChar() // 替换反引号 name = strings.ReplaceAll(name, "`", quoteChar) // 如果目标是反引号,需要替换双引号 if quoteChar == "`" { name = strings.ReplaceAll(name, "\"", quoteChar) } return name } // GetDialect 获取方言 func (p *IdentifierProcessor) GetDialect() Dialect { return p.dialect } // GetPrefix 获取前缀 func (p *IdentifierProcessor) GetPrefix() string { return p.prefix }