fix(db): 修复表列标识符处理逻辑确保聚合函数正常工作
- 修改 ProcessColumn 方法,table.column 格式不再添加反引号,只添加前缀 - 修改 ProcessColumnNoPrefix 方法,table.column 格式不加引号保持原始格式 - 更新 ProcessConditionString 方法,条件字符串中的 table.column 不加反引号 - 修复聚合函数如 Sum、Avg、Max、Min 在 table.column 格式下的正确性 - 添加完整的 table.column 格式测试用例验证功能一致性 - 确保返回的列名与原始列名一致,便于聚合函数等场景读取结果
This commit is contained in:
parent
9949231d7c
commit
c922023740
@ -83,21 +83,22 @@ func (p *IdentifierProcessor) ProcessTableNameNoPrefix(name string) string {
|
|||||||
|
|
||||||
// ProcessColumn 处理 table.column 格式
|
// ProcessColumn 处理 table.column 格式
|
||||||
// 输入: "name" 或 "order.name" 或 "`order`.name" 或 "`order`.`name`"
|
// 输入: "name" 或 "order.name" 或 "`order`.name" 或 "`order`.`name`"
|
||||||
// 输出: "`name`" 或 "`app_order`.`name`" (MySQL)
|
// 输出: "`name`" 或 "app_order.name"
|
||||||
|
// 注意: 单独的列名加引号(避免关键字冲突),table.column 格式不加引号
|
||||||
func (p *IdentifierProcessor) ProcessColumn(name string) string {
|
func (p *IdentifierProcessor) ProcessColumn(name string) string {
|
||||||
// 检查是否包含点号
|
// 检查是否包含点号
|
||||||
if !strings.Contains(name, ".") {
|
if !strings.Contains(name, ".") {
|
||||||
// 单独的列名,只加引号
|
// 单独的列名,需要加引号(避免关键字冲突)
|
||||||
return p.dialect.QuoteIdentifier(p.stripQuotes(name))
|
return p.dialect.QuoteIdentifier(p.stripQuotes(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理 table.column 格式
|
// 处理 table.column 格式,不加引号,只添加前缀
|
||||||
parts := p.splitTableColumn(name)
|
parts := p.splitTableColumn(name)
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
tableName := p.stripQuotes(parts[0])
|
tableName := p.stripQuotes(parts[0])
|
||||||
columnName := p.stripQuotes(parts[1])
|
columnName := p.stripQuotes(parts[1])
|
||||||
// 表名添加前缀
|
// table.column 格式不加反引号
|
||||||
return p.dialect.QuoteIdentifier(p.prefix+tableName) + "." + p.dialect.QuoteIdentifier(columnName)
|
return p.prefix + tableName + "." + columnName
|
||||||
}
|
}
|
||||||
|
|
||||||
// 无法解析,返回原样但转换引号
|
// 无法解析,返回原样但转换引号
|
||||||
@ -107,14 +108,16 @@ func (p *IdentifierProcessor) ProcessColumn(name string) string {
|
|||||||
// ProcessColumnNoPrefix 处理 table.column 格式(不添加前缀)
|
// ProcessColumnNoPrefix 处理 table.column 格式(不添加前缀)
|
||||||
func (p *IdentifierProcessor) ProcessColumnNoPrefix(name string) string {
|
func (p *IdentifierProcessor) ProcessColumnNoPrefix(name string) string {
|
||||||
if !strings.Contains(name, ".") {
|
if !strings.Contains(name, ".") {
|
||||||
|
// 单独的列名,需要加引号(避免关键字冲突)
|
||||||
return p.dialect.QuoteIdentifier(p.stripQuotes(name))
|
return p.dialect.QuoteIdentifier(p.stripQuotes(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// table.column 格式不加引号
|
||||||
parts := p.splitTableColumn(name)
|
parts := p.splitTableColumn(name)
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
tableName := p.stripQuotes(parts[0])
|
tableName := p.stripQuotes(parts[0])
|
||||||
columnName := p.stripQuotes(parts[1])
|
columnName := p.stripQuotes(parts[1])
|
||||||
return p.dialect.QuoteIdentifier(tableName) + "." + p.dialect.QuoteIdentifier(columnName)
|
return tableName + "." + columnName
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.convertQuotes(name)
|
return p.convertQuotes(name)
|
||||||
@ -122,7 +125,9 @@ func (p *IdentifierProcessor) ProcessColumnNoPrefix(name string) string {
|
|||||||
|
|
||||||
// ProcessConditionString 智能解析条件字符串(如 ON 条件)
|
// ProcessConditionString 智能解析条件字符串(如 ON 条件)
|
||||||
// 输入: "user.id = order.user_id AND order.status = 1"
|
// 输入: "user.id = order.user_id AND order.status = 1"
|
||||||
// 输出: "`app_user`.`id` = `app_order`.`user_id` AND `app_order`.`status` = 1" (MySQL)
|
// 输出: "app_user.id = app_order.user_id AND app_order.status = 1"
|
||||||
|
// 注意: table.column 格式不加反引号,因为 MySQL/SQLite/PostgreSQL 都能正确解析
|
||||||
|
// 这样可以保持返回的列名与原始列名一致,便于聚合函数等场景读取结果
|
||||||
func (p *IdentifierProcessor) ProcessConditionString(condition string) string {
|
func (p *IdentifierProcessor) ProcessConditionString(condition string) string {
|
||||||
if condition == "" {
|
if condition == "" {
|
||||||
return condition
|
return condition
|
||||||
@ -131,20 +136,21 @@ func (p *IdentifierProcessor) ProcessConditionString(condition string) string {
|
|||||||
result := condition
|
result := condition
|
||||||
|
|
||||||
// 首先处理已有完整引号的情况 `table`.`column` 或 "table"."column"
|
// 首先处理已有完整引号的情况 `table`.`column` 或 "table"."column"
|
||||||
// 这些需要先处理,因为它们的格式最明确
|
// 去除引号,只添加前缀
|
||||||
fullyQuotedPattern := regexp.MustCompile("[`\"]([a-zA-Z_][a-zA-Z0-9_]*)[`\"]\\.[`\"]([a-zA-Z_][a-zA-Z0-9_]*)[`\"]")
|
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 {
|
result = fullyQuotedPattern.ReplaceAllStringFunc(result, func(match string) string {
|
||||||
parts := fullyQuotedPattern.FindStringSubmatch(match)
|
parts := fullyQuotedPattern.FindStringSubmatch(match)
|
||||||
if len(parts) == 3 {
|
if len(parts) == 3 {
|
||||||
tableName := parts[1]
|
tableName := parts[1]
|
||||||
colName := parts[2]
|
colName := parts[2]
|
||||||
return p.dialect.QuoteIdentifier(p.prefix+tableName) + "." + p.dialect.QuoteIdentifier(colName)
|
// table.column 格式不加反引号,只添加前缀
|
||||||
|
return p.prefix + tableName + "." + colName
|
||||||
}
|
}
|
||||||
return match
|
return match
|
||||||
})
|
})
|
||||||
|
|
||||||
// 然后处理部分引号的情况 `table`.column 或 "table".column
|
// 然后处理部分引号的情况 `table`.column 或 "table".column
|
||||||
// 注意:需要避免匹配已处理的内容(已经是双引号包裹的)
|
// 去除引号,只添加前缀
|
||||||
quotedTablePattern := regexp.MustCompile("[`\"]([a-zA-Z_][a-zA-Z0-9_]*)[`\"]\\.([a-zA-Z_][a-zA-Z0-9_]*)(?:[^`\"]|$)")
|
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 {
|
result = quotedTablePattern.ReplaceAllStringFunc(result, func(match string) string {
|
||||||
parts := quotedTablePattern.FindStringSubmatch(match)
|
parts := quotedTablePattern.FindStringSubmatch(match)
|
||||||
@ -159,7 +165,8 @@ func (p *IdentifierProcessor) ProcessConditionString(condition string) string {
|
|||||||
suffix = string(lastChar)
|
suffix = string(lastChar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return p.dialect.QuoteIdentifier(p.prefix+tableName) + "." + p.dialect.QuoteIdentifier(colName) + suffix
|
// table.column 格式不加反引号,只添加前缀
|
||||||
|
return p.prefix + tableName + "." + colName + suffix
|
||||||
}
|
}
|
||||||
return match
|
return match
|
||||||
})
|
})
|
||||||
@ -174,7 +181,8 @@ func (p *IdentifierProcessor) ProcessConditionString(condition string) string {
|
|||||||
tableName := parts[2]
|
tableName := parts[2]
|
||||||
colName := parts[3]
|
colName := parts[3]
|
||||||
suffix := parts[4] // 后面的边界字符
|
suffix := parts[4] // 后面的边界字符
|
||||||
return prefix + p.dialect.QuoteIdentifier(p.prefix+tableName) + "." + p.dialect.QuoteIdentifier(colName) + suffix
|
// table.column 格式不加反引号,只添加前缀
|
||||||
|
return prefix + p.prefix + tableName + "." + colName + suffix
|
||||||
}
|
}
|
||||||
return match
|
return match
|
||||||
})
|
})
|
||||||
|
|||||||
131
example/main.go
131
example/main.go
@ -564,32 +564,36 @@ func testAggregate(that *Context) Map {
|
|||||||
test2["count"] = count2
|
test2["count"] = count2
|
||||||
tests = append(tests, test2)
|
tests = append(tests, test2)
|
||||||
|
|
||||||
// 5.3 Sum 求和
|
// 5.3 Sum 求和 - 单字段名
|
||||||
test3 := Map{"name": "Sum 求和"}
|
test3 := Map{"name": "Sum 求和 (单字段名)"}
|
||||||
sum3 := that.Db.Sum("article", "click_num", Map{"state": 0})
|
sum3 := that.Db.Sum("article", "click_num", Map{"state": 0})
|
||||||
test3["result"] = sum3 >= 0
|
test3["result"] = sum3 >= 0
|
||||||
test3["sum"] = sum3
|
test3["sum"] = sum3
|
||||||
|
test3["lastQuery"] = that.Db.LastQuery
|
||||||
tests = append(tests, test3)
|
tests = append(tests, test3)
|
||||||
|
|
||||||
// 5.4 Avg 平均值
|
// 5.4 Avg 平均值 - 单字段名
|
||||||
test4 := Map{"name": "Avg 平均值"}
|
test4 := Map{"name": "Avg 平均值 (单字段名)"}
|
||||||
avg4 := that.Db.Avg("article", "click_num", Map{"state": 0})
|
avg4 := that.Db.Avg("article", "click_num", Map{"state": 0})
|
||||||
test4["result"] = avg4 >= 0
|
test4["result"] = avg4 >= 0
|
||||||
test4["avg"] = avg4
|
test4["avg"] = avg4
|
||||||
|
test4["lastQuery"] = that.Db.LastQuery
|
||||||
tests = append(tests, test4)
|
tests = append(tests, test4)
|
||||||
|
|
||||||
// 5.5 Max 最大值
|
// 5.5 Max 最大值 - 单字段名
|
||||||
test5 := Map{"name": "Max 最大值"}
|
test5 := Map{"name": "Max 最大值 (单字段名)"}
|
||||||
max5 := that.Db.Max("article", "click_num", Map{"state": 0})
|
max5 := that.Db.Max("article", "click_num", Map{"state": 0})
|
||||||
test5["result"] = max5 >= 0
|
test5["result"] = max5 >= 0
|
||||||
test5["max"] = max5
|
test5["max"] = max5
|
||||||
|
test5["lastQuery"] = that.Db.LastQuery
|
||||||
tests = append(tests, test5)
|
tests = append(tests, test5)
|
||||||
|
|
||||||
// 5.6 Min 最小值
|
// 5.6 Min 最小值 - 单字段名
|
||||||
test6 := Map{"name": "Min 最小值"}
|
test6 := Map{"name": "Min 最小值 (单字段名)"}
|
||||||
min6 := that.Db.Min("article", "sort", Map{"state": 0})
|
min6 := that.Db.Min("article", "sort", Map{"state": 0})
|
||||||
test6["result"] = true // sort 可能为 0
|
test6["result"] = true // sort 可能为 0
|
||||||
test6["min"] = min6
|
test6["min"] = min6
|
||||||
|
test6["lastQuery"] = that.Db.LastQuery
|
||||||
tests = append(tests, test6)
|
tests = append(tests, test6)
|
||||||
|
|
||||||
// 5.7 GROUP BY 分组统计
|
// 5.7 GROUP BY 分组统计
|
||||||
@ -606,6 +610,117 @@ func testAggregate(that *Context) Map {
|
|||||||
test7["stats"] = stats7
|
test7["stats"] = stats7
|
||||||
tests = append(tests, test7)
|
tests = append(tests, test7)
|
||||||
|
|
||||||
|
// ==================== 新增:table.column 格式测试 ====================
|
||||||
|
// 5.8 Sum 求和 - table.column 格式(修复验证)
|
||||||
|
test8 := Map{"name": "Sum 求和 (table.column 格式)"}
|
||||||
|
sum8 := that.Db.Sum("article", "article.click_num", Map{"state": 0})
|
||||||
|
test8["result"] = sum8 >= 0
|
||||||
|
test8["sum"] = sum8
|
||||||
|
test8["expected"] = "与单字段名 Sum 结果相同"
|
||||||
|
test8["match_single_field"] = sum8 == sum3 // 应该与 test3 结果相同
|
||||||
|
test8["lastQuery"] = that.Db.LastQuery
|
||||||
|
tests = append(tests, test8)
|
||||||
|
|
||||||
|
// 5.9 Avg 平均值 - table.column 格式
|
||||||
|
test9 := Map{"name": "Avg 平均值 (table.column 格式)"}
|
||||||
|
avg9 := that.Db.Avg("article", "article.click_num", Map{"state": 0})
|
||||||
|
test9["result"] = avg9 >= 0
|
||||||
|
test9["avg"] = avg9
|
||||||
|
test9["match_single_field"] = avg9 == avg4
|
||||||
|
test9["lastQuery"] = that.Db.LastQuery
|
||||||
|
tests = append(tests, test9)
|
||||||
|
|
||||||
|
// 5.10 Max 最大值 - table.column 格式
|
||||||
|
test10 := Map{"name": "Max 最大值 (table.column 格式)"}
|
||||||
|
max10 := that.Db.Max("article", "article.click_num", Map{"state": 0})
|
||||||
|
test10["result"] = max10 >= 0
|
||||||
|
test10["max"] = max10
|
||||||
|
test10["match_single_field"] = max10 == max5
|
||||||
|
test10["lastQuery"] = that.Db.LastQuery
|
||||||
|
tests = append(tests, test10)
|
||||||
|
|
||||||
|
// 5.11 Min 最小值 - table.column 格式
|
||||||
|
test11 := Map{"name": "Min 最小值 (table.column 格式)"}
|
||||||
|
min11 := that.Db.Min("article", "article.sort", Map{"state": 0})
|
||||||
|
test11["result"] = true
|
||||||
|
test11["min"] = min11
|
||||||
|
test11["match_single_field"] = min11 == min6
|
||||||
|
test11["lastQuery"] = that.Db.LastQuery
|
||||||
|
tests = append(tests, test11)
|
||||||
|
|
||||||
|
// ==================== 带 JOIN 的聚合函数测试 ====================
|
||||||
|
// 5.12 Sum 带 JOIN - table.column 格式
|
||||||
|
test12 := Map{"name": "Sum 带 JOIN (table.column 格式)"}
|
||||||
|
joinSlice := Slice{
|
||||||
|
Map{"[>]ctg": "article.ctg_id = ctg.id"},
|
||||||
|
}
|
||||||
|
sum12 := that.Db.Sum("article", "article.click_num", joinSlice, Map{"article.state": 0})
|
||||||
|
test12["result"] = sum12 >= 0
|
||||||
|
test12["sum"] = sum12
|
||||||
|
test12["lastQuery"] = that.Db.LastQuery
|
||||||
|
tests = append(tests, test12)
|
||||||
|
|
||||||
|
// 5.13 Count 带 JOIN
|
||||||
|
test13 := Map{"name": "Count 带 JOIN"}
|
||||||
|
count13 := that.Db.Count("article", joinSlice, Map{"article.state": 0})
|
||||||
|
test13["result"] = count13 >= 0
|
||||||
|
test13["count"] = count13
|
||||||
|
test13["lastQuery"] = that.Db.LastQuery
|
||||||
|
tests = append(tests, test13)
|
||||||
|
|
||||||
|
// ==================== Select 方法验证 table.column 格式 ====================
|
||||||
|
// 5.14 Select 使用 table.column 选择字段
|
||||||
|
test14 := Map{"name": "Select table.column 字段选择"}
|
||||||
|
articles14 := that.Db.Select("article",
|
||||||
|
"article.id, article.title, article.click_num",
|
||||||
|
Map{"article.state": 0, "LIMIT": 3})
|
||||||
|
test14["result"] = len(articles14) >= 0
|
||||||
|
test14["count"] = len(articles14)
|
||||||
|
// 验证返回的 Map 中字段名是否正确(不带反引号)
|
||||||
|
if len(articles14) > 0 {
|
||||||
|
keys := []string{}
|
||||||
|
for k := range articles14[0] {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
test14["returned_keys"] = keys
|
||||||
|
// 检查是否能正确读取值(字段名是 article.id 还是 id)
|
||||||
|
test14["sample_data"] = articles14[0]
|
||||||
|
}
|
||||||
|
test14["lastQuery"] = that.Db.LastQuery
|
||||||
|
tests = append(tests, test14)
|
||||||
|
|
||||||
|
// 5.15 Select 带 JOIN 使用 table.column
|
||||||
|
test15 := Map{"name": "Select 带 JOIN table.column"}
|
||||||
|
articles15 := that.Db.Select("article",
|
||||||
|
joinSlice,
|
||||||
|
"article.id, article.title, ctg.name as ctg_name",
|
||||||
|
Map{"article.state": 0, "LIMIT": 3})
|
||||||
|
test15["result"] = len(articles15) >= 0
|
||||||
|
test15["count"] = len(articles15)
|
||||||
|
if len(articles15) > 0 {
|
||||||
|
keys := []string{}
|
||||||
|
for k := range articles15[0] {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
test15["returned_keys"] = keys
|
||||||
|
test15["sample_data"] = articles15[0]
|
||||||
|
}
|
||||||
|
test15["lastQuery"] = that.Db.LastQuery
|
||||||
|
tests = append(tests, test15)
|
||||||
|
|
||||||
|
// ==================== 聚合函数结果对比验证 ====================
|
||||||
|
// 5.16 验证 table.column 与单字段结果一致性
|
||||||
|
test16 := Map{"name": "聚合函数一致性验证"}
|
||||||
|
allMatch := (sum8 == sum3) && (avg9 == avg4) && (max10 == max5) && (min11 == min6)
|
||||||
|
test16["result"] = allMatch
|
||||||
|
test16["sum_match"] = sum8 == sum3
|
||||||
|
test16["avg_match"] = avg9 == avg4
|
||||||
|
test16["max_match"] = max10 == max5
|
||||||
|
test16["min_match"] = min11 == min6
|
||||||
|
test16["summary"] = fmt.Sprintf("Sum: %v=%v, Avg: %v=%v, Max: %v=%v, Min: %v=%v",
|
||||||
|
sum3, sum8, avg4, avg9, max5, max10, min6, min11)
|
||||||
|
tests = append(tests, test16)
|
||||||
|
|
||||||
result["tests"] = tests
|
result["tests"] = tests
|
||||||
result["success"] = true
|
result["success"] = true
|
||||||
return result
|
return result
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user