From c922023740216621ccba77534099911db53c56a1 Mon Sep 17 00:00:00 2001 From: hoteas <925970985@qq.com> Date: Mon, 2 Feb 2026 11:02:59 +0800 Subject: [PATCH] =?UTF-8?q?fix(db):=20=E4=BF=AE=E5=A4=8D=E8=A1=A8=E5=88=97?= =?UTF-8?q?=E6=A0=87=E8=AF=86=E7=AC=A6=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E7=A1=AE=E4=BF=9D=E8=81=9A=E5=90=88=E5=87=BD=E6=95=B0=E6=AD=A3?= =?UTF-8?q?=E5=B8=B8=E5=B7=A5=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 ProcessColumn 方法,table.column 格式不再添加反引号,只添加前缀 - 修改 ProcessColumnNoPrefix 方法,table.column 格式不加引号保持原始格式 - 更新 ProcessConditionString 方法,条件字符串中的 table.column 不加反引号 - 修复聚合函数如 Sum、Avg、Max、Min 在 table.column 格式下的正确性 - 添加完整的 table.column 格式测试用例验证功能一致性 - 确保返回的列名与原始列名一致,便于聚合函数等场景读取结果 --- db/identifier.go | 32 +++++++----- example/main.go | 131 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 143 insertions(+), 20 deletions(-) diff --git a/db/identifier.go b/db/identifier.go index e507ed4..fcd0d11 100644 --- a/db/identifier.go +++ b/db/identifier.go @@ -83,21 +83,22 @@ func (p *IdentifierProcessor) ProcessTableNameNoPrefix(name string) string { // ProcessColumn 处理 table.column 格式 // 输入: "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 { // 检查是否包含点号 if !strings.Contains(name, ".") { - // 单独的列名,只加引号 + // 单独的列名,需要加引号(避免关键字冲突) return p.dialect.QuoteIdentifier(p.stripQuotes(name)) } - // 处理 table.column 格式 + // 处理 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) + // table.column 格式不加反引号 + return p.prefix + tableName + "." + columnName } // 无法解析,返回原样但转换引号 @@ -107,14 +108,16 @@ func (p *IdentifierProcessor) ProcessColumn(name string) string { // ProcessColumnNoPrefix 处理 table.column 格式(不添加前缀) func (p *IdentifierProcessor) ProcessColumnNoPrefix(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(tableName) + "." + p.dialect.QuoteIdentifier(columnName) + return tableName + "." + columnName } return p.convertQuotes(name) @@ -122,7 +125,9 @@ func (p *IdentifierProcessor) ProcessColumnNoPrefix(name string) string { // ProcessConditionString 智能解析条件字符串(如 ON 条件) // 输入: "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 { if condition == "" { return condition @@ -131,20 +136,21 @@ func (p *IdentifierProcessor) ProcessConditionString(condition string) string { 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) + // table.column 格式不加反引号,只添加前缀 + return p.prefix + tableName + "." + 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) @@ -159,7 +165,8 @@ func (p *IdentifierProcessor) ProcessConditionString(condition string) string { 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 }) @@ -174,7 +181,8 @@ func (p *IdentifierProcessor) ProcessConditionString(condition string) string { tableName := parts[2] colName := parts[3] 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 }) diff --git a/example/main.go b/example/main.go index 3ccffe3..779b6a5 100644 --- a/example/main.go +++ b/example/main.go @@ -564,32 +564,36 @@ func testAggregate(that *Context) Map { test2["count"] = count2 tests = append(tests, test2) - // 5.3 Sum 求和 - test3 := Map{"name": "Sum 求和"} + // 5.3 Sum 求和 - 单字段名 + test3 := Map{"name": "Sum 求和 (单字段名)"} sum3 := that.Db.Sum("article", "click_num", Map{"state": 0}) test3["result"] = sum3 >= 0 test3["sum"] = sum3 + test3["lastQuery"] = that.Db.LastQuery tests = append(tests, test3) - // 5.4 Avg 平均值 - test4 := Map{"name": "Avg 平均值"} + // 5.4 Avg 平均值 - 单字段名 + test4 := Map{"name": "Avg 平均值 (单字段名)"} avg4 := that.Db.Avg("article", "click_num", Map{"state": 0}) test4["result"] = avg4 >= 0 test4["avg"] = avg4 + test4["lastQuery"] = that.Db.LastQuery tests = append(tests, test4) - // 5.5 Max 最大值 - test5 := Map{"name": "Max 最大值"} + // 5.5 Max 最大值 - 单字段名 + test5 := Map{"name": "Max 最大值 (单字段名)"} max5 := that.Db.Max("article", "click_num", Map{"state": 0}) test5["result"] = max5 >= 0 test5["max"] = max5 + test5["lastQuery"] = that.Db.LastQuery tests = append(tests, test5) - // 5.6 Min 最小值 - test6 := Map{"name": "Min 最小值"} + // 5.6 Min 最小值 - 单字段名 + test6 := Map{"name": "Min 最小值 (单字段名)"} min6 := that.Db.Min("article", "sort", Map{"state": 0}) test6["result"] = true // sort 可能为 0 test6["min"] = min6 + test6["lastQuery"] = that.Db.LastQuery tests = append(tests, test6) // 5.7 GROUP BY 分组统计 @@ -606,6 +610,117 @@ func testAggregate(that *Context) Map { test7["stats"] = stats7 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["success"] = true return result